IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

linux后端服务程序之信号处理

淘宝核心系统团队博客 2012-09-18 23:40:26 累计浏览 4,814 次
本机暂存

信号就是通知某个进程发了某个事件,也称为软件中断。信号提供了一种处理异步事件的方法。信号通常是异步发生的,进程预先不知道信号准确发生的时刻。后端程序(daemon)往往需要提供7*24不间断的服务,因此,编程daemon程序时对信号的正确处理尤为重要。

下面和大家分享编写daemon程序时信号处理的注意事项,内容都来自Internet,只是进行了整理和总结。关于信号的基础只是请参考APUE。

常见的信号

    SIGHUP 1 和终端的连接断开,发送该信号给控制进程。通常用此信号来通知daemon重新读取配置文件(因为daemon不会有控制终端,通常不会收到该信号)。

     SIGINT 2 用户中断(Ctrl + C)。

SIGABRT 6 调用abort函数产生(通常是自杀)。

SIGKILL 9 可以杀死任意进程,不能被捕获或忽略(俗称酒杀)。

    SIGSEGV 11 无效的内存引用(segmentation fault)。

     SIGPIPE 13 对于socket fd,当一个进程向某个已经收到RST的fd执行写操作时,内核会向该进程发送该信号。

     SIGTERM 15 kill命令发送的默认终止信号。

     SIGCHLD 17 进程终止时向其父进程发送的信号。

     SIGPROF 27 使用gprof工具测试时会收到该信号。

别用signal,用sigaction

    可以通过signal或者sigaction函数来设置信号处理函数,但signal函数太过古老,因此推荐使用sigaction。理由如下:

1. sigaction可以提供更多接收到信号的信息。

    2. 调用完信号处理函数后重新设置处理函数不会对sigaction有影响,因为sigaction默认是不会去重置处理函数的,同时在执行处理函数会屏蔽掉该信号,也不会有竞争。

     3. signal函数在某些系统中会默认重启被中断的系统调用,而sigaction默认不会这样做。

     4. signal函数在多线程环境中的行为是未定义的,必须使用sigaction函数。

数据传输和信号

    使用了系统调用的函数都有可能被信号中断,立刻返回的函数(不需要等待I/O操作的完成或sleep)不会被中断,而需要等待的函数(等待网络传输,管道的读或者sleep)将会被中断,比如select, read, connect。

     在daemon程序中,恰当地处理被中断的系统调用是非常重要的。如果read, write等传输数据的函数被中断,必须处理这种情况并恢复数据的传输。有两种被中断的场景:

     1. 当没有数据传输时就被中断,函数返回-1,这时可以通过判断errno的值来识别这种错误,如果errno == EINTR,则表示函数在没有任何数据传输的情况下就被中断,这时可以通过同样的参数来再次调用该数据。

     2. 另一种情况是数据传输已经在进行,但在没有完成之前被中断;这种情况下函数不会返回错误,而是返回一个小于期望大小的值,同时errno也不会有错误设置,想识别这种情况只能捕获导致中断的信号。在中断之后恢复数据传输时一定记得部分数据已经被传输,必须从正确的偏移再次发起传输。

     不要通过sigaction函数设置SA_RESTART来处理被中断的系统调用。

多线程和信号

多线程程序的信号处理和单线程程序有很大的区别。根据POSIX规范,一个多线程的程序只有一个进程和一个pid,哪个线程会被中断并处理到达的信号呢?有两种情况:

  • 发送给进程的信号 比如用kill向一个pid发送信号,每个线程都有单独的信号掩码,可以通过pthread_sigmask来设置。信号不会分发给已经屏蔽了该信号的线程,而是在没有屏蔽该信号的线程中任选一个来接收,但没有指定哪个线程一定可以接收到。如果所有的线程都屏蔽了该信号,该信号将在预处理队列中排队。
  • 发送给线程的信号 pthread_kill可以用来向指定的线程发送信号,分发和排队都是线程级别的。如果没有指定信号处理函数,而默认是结束进程,发送给线程的信号也会导致整个进程退出。
  • 不要在信号处理函数中使用锁。

    同步化信号处理 signalfd

        signalfd是一个在linux kernel 2.6.22提供的系统调用,功能是使用一个fd来接收信号。这样就可以同步地处理信号,也不需要设置处理函数。可以man signalfd 查看示例程序。首先必须使用sigprocmask来屏蔽要使用signalfd来处理的信号,然后调用signalfd创建一个fd用来读取到达的信号。当被屏蔽的信号到达时,程序将不会被中断,也不会有处理函数被调用。信号会在fd中排队。signalfd创建的fd可以和其他fd一样:可以放在select, poll, epoll中;可以设置为非阻塞;可以为不同的信号创建不同的fd;在fork之后该fd也不会关闭掉,子进程同样可以读懂发送给父进程的信号。signalfd非常适合在主循环中执行epoll处理大量连接的单进程网络服务程序中使用,信号的处理可以和其他fd一样加到epoll中。由于程序不会被中断,可以选择合适的时机才去处理信号。

    同分类推荐文章

    1. 等了十年的 Go 链式管道,终于来了:seq 让你像写 Scala 一样写 Go (2026-06-25 18:38:18)
    2. Go 实验特性详解 (2026-06-21 10:05:27)
    3. amd64 微架构级别对 Go 程序性能提升多少? (2026-06-21 09:38:49)

    查看更多 后端 文章 →

    建议继续学习

    1. Linux如何统计进程的CPU利用率 (累计阅读 16,308)
    2. 我的 RHCA 之路 (累计阅读 14,014)
    3. Linux内存点滴 用户进程内存空间 (累计阅读 13,232)
    4. 给程序员新手的一些建议 (累计阅读 13,090)
    5. Linux 性能监控、测试、优化工具 (累计阅读 13,013)
    6. 关于linux内存free的一些事情 (累计阅读 12,870)
    7. ps - 按进程消耗内存多少排序 (累计阅读 12,691)
    8. Google怎么用linux (累计阅读 12,583)
    9. Linux Used内存到底哪里去了? (累计阅读 11,868)
    10. find命令的一点注意事项 (累计阅读 11,867)