03-信号类型和默认行为
20.2 信号类型和默认行为
此前曾提及,Linux对标准信号的编号为1~31。然而,Linux于signal(7)手册页中列出的信号名称却超出了31个。名称超出的原因有多种。有些名称只是其他名称的同义词,之所以定义是为了与其他UNIX实现保持源码兼容。其他名称虽然有定义,但却并未使用。以下列表介绍了各种信号。
SIGABRT
当进程调用abort()函数(21.2.2节)时,系统向进程发送该信号。默认情况下,该信号会终止进程,并产生核心转储文件。这实现了调用abort()的预期目标,产生核心转储文件用于调试。
SIGALRM
经调用alarm()或setitimer()而设置的实时定时器一旦到期,内核将产生该信号。实时定时器是根据挂钟时间进行计时的(即人类对逝去时间的概念)。更多细节参见23.1节。
SIGBUS
产生该信号(总线错误,bus error)即表示发生了某种内存访问错误。如49.4.3节所述,当使用由mmap()所创建的内存映射时,如果试图访问的地址超出了底层内存映射文件的结尾,那么将产生该错误。
SIGCHLD
当父进程的某一子进程终止(或者因为调用了exit(),或者因为被信号杀死)时,(内核)将向父进程发送该信号。当父进程的某一子进程因收到信号而停止或恢复时,也可能会向父进程发送该信号。详情请参考26.3节。
SIGCLD
与SIGCHLD信号同义。
SIGCONT
将该信号发送给已停止的进程,进程将会恢复运行(即在之后某个时间点重新获得调度)。当接收信号的进程当前不处于停止状态时,默认情况下将忽略该信号。进程可以捕获该信号,以便在恢复运行时可以执行某些操作。关于该信号的更多细节请参考22.2节和34.7节。
SIGEMT
UNIX系统通常用该信号来标识一个依赖于实现的硬件错误。Linux系统仅在Sun SPARC实现中使用了该信号。后缀EMT源自仿真器陷阱(emulator trap),Digital PDP-11的汇编程序助记符之一。
SIGFPE
该信号因特定类型的算术错误而产生,比如除以0。后缀FPE是浮点异常的缩写,不过整型算术错误也能产生该信号。该信号于何时产生的精确细节取决于硬件架构和对CPU控制寄存器的设置。例如,在x86-32架构中,整数除以0总是产生SIGFPE信号,但是对浮点数除以 0 的处理则取决于是否启用了 FE_DIVBYZERO 异常。如果启用了该异常(使用feenableexcept()),那么浮点数除以0也将产生SIGFPE信号,否则,将为操作数产生符合IEEE标准的结果(无穷大的浮点表示形式)。更多信息请参考fenv(3)手册页和<fenv.h>文件。
SIGHUP
当终端断开(挂机)时,将发送该信号给终端控制进程。34.6节将描述控制进程的概念以及产生SIGHUP信号的各种环境。SIGHUP信号还可用于守护进程(比如,init、httpd和inetd)。许多守护进程会在收到SIGHUP信号时重新进行初始化并重读配置文件。借助于显式执行kill命令或者运行同等功效的程序或脚本,系统管理员可向守护进程手工发送SIGHUP信号来触发这些行为。
SIGILL
如果进程试图执行非法(即格式不正确)的机器语言指令,系统将向进程发送该信号。
SIGINFO
在Linux中,该信号名与SIGPWR信号名同义。在BSD系统中,键入Control-T可产生SIGINFO信号,用于获取前台进程组的状态信息。
SIGINT
当用户键入终端中断字符(通常为Control-C)时,终端驱动程序将发送该信号给前台进程组。该信号的默认行为是终止进程。
SIGIO
利用fcntl()系统调用,即可于特定类型(诸如终端和套接字)的打开文件描述符发生I/O事件时产生该信号。63.3节将就此特性做进一步说明。
SIGIOT
在Linux中,该信号名与SIGABRT信号同义。在其他一些UNIX实现中,该信号表示发生了由实现定义的硬件错误。
SIGKILL
此信号为“必杀(sure kill)”信号,处理器程序无法将其阻塞、忽略或者捕获,故而“一击必杀”,总能终止进程。
SIGLOST
Linux中存在该信号名,但并未加以使用。在其他一些UNIX实现中,如果远端NFS服务器在崩溃之后重新恢复,而NFS客户端却未能重新获得由本地进程所持有的锁,那么NFS客户端将向这些进程发送此信号。(NFS规范并未对该特性进行标准化。)
SIGPIPE
当某一进程试图向管道、FIFO或套接字写入信息时,如果这些设备并无相应的阅读进程,那么系统将产生该信号。之所以如此,通常是因为阅读进程已经关闭了其作为IPC通道的文件描述符。更多细节请参考44.2节。
SIGPOLL
该信号从System V派生而来,与Linux中的SIGIO信号同义。
SIGPROF
由setitimer()调用(参见23.1节)所设置的性能分析定时器刚一过期,内核就将产生该信号。性能分析定时器用于记录进程所使用的CPU时间。与虚拟定时器不同(参见下面的SIGVTALRM信号),性能分析定时器在对CPU时间计数时会将用户态与内核态都包含在内。
SIGPWR
这是电源故障信号。当系统配备有不间断电源(UPS)时,可以设置守护进程来监控电源发生故障时备用电池的剩余电量。如果电池电量行将耗尽(长时间停电之后),那么监控进程会将该信号发往init进程,而后者则将其解读为快速、有序关闭系统的一个请求。
SIGQUIT
当用户在键盘上键入退出字符(通常为Control-\)时,该信号将发往前台进程组。默认情况下,该信号终止进程,并生成可用于调试的核心转储文件。进程如果陷入无限循环,或者不再响应时,使用SIGQUIT信号就很合适。键入Control-\,再调用gdb调试器加载刚才生成的核心转储文件,接着用backtrace命令来获取堆栈跟踪信息,就能发现正在执行的是程序的哪部分代码。([Matloff, 2008]描述了gdb的用法。)
SIGSEGV
这一信号非常常见,当应用程序对内存的引用无效时,就会产生该信号。引起对内存无效引用的原因很多,可能是因为要引用的页不存在(例如,该页位于堆和栈之间的未映射区域),或者进程试图更新只读内存(比如,程序文本段或者标记为只读的一块映射内存区域)中某一位置的内容,又或者进程企图在用户态(参见2.1节)去访问内核的部分内存。C语言中引发这些事件的往往是解引用的指针里包含了错误地址(例如,未初始化的指针),或者传递了一个无效参数供函数调用。该信号的命名源于术语“段违例”。
SIGSTKFLT
signal(7)手册页中将其记载为“协处理器栈错误”,Linux对该信号作了定义,但并未加以使用。
SIGSTOP
这是一个必停(sure stop)信号,处理器程序无法将其阻塞、忽略或者捕获,故而总是能停止进程。
SIGSYS
如果进程发起的系统调用有误,那么将产生该信号。这意味着系统将进程执行的指令视为一个系统调用陷阱(trap),但相关的系统调用编号却是无效的(参见3.1节)。
SIGTERM
这是用来终止进程的标准信号,也是kill和killall命令所发送的默认信号。用户有时会使用kill-KILL或者kill-9显式向进程发送SIGKILL信号。然而,这一做法通常是错误的。精心设计的应用程序应当为SIGTERM信号设置处理器程序,以便于其能够预先清除临时文件和释放其他资源,从而全身而退。发送SIGKILL信号可以杀掉某个进程,从而绕开了SIGTERM信号的处理器程序。因此,总是应该首先尝试使用SIGTERM信号来终止进程,而把SIGKILL信号作为最后手段,去对付那些不响应SIGTERM信号的失控进程。
SIGTRAP
该信号用来实现断点调试功能以及strace(1)命令(附录A)所执行的跟踪系统调用功能。更多信息参见ptrace(2)手册页。
SIGTSTP
这是作业控制的停止信号,当用户在键盘上输入挂起字符(通常是Control-Z)时,将发送该信号给前台进程组,使其停止运行。第34章详细描述了进程组(作业)和作业控制,以及程序应在何时以及如何去处理该信号。该信号名源自“终端停止(terminal stop)”的术语。
SIGTTIN
在作业控制shell下运行时,若后台进程组试图对终端进行read()操作,终端驱动程序则将向该进程组发送此信号。该信号默认将停止进程。
SIGTTOU
该信号的目的与SIGTTIN信号类似,但所针对的是后台作业的终端输出。在作业控制shell下运行时,如果对终端启用了TOSTOP(终端输出停止)选项(可能是通过stty tostop命令),而某一后台进程组试图对终端进行write()操作(参见34.7.1节),那么终端驱动程序将向该进程组发送SIGTTOU信号。该信号默认将停止进程。
SIGUNUSED
顾名思义,该信号没有使用。在 Linux 2.4 及其后续版本中,该信号名在很多架构中与SIGSYS信号同义。换言之,尽管信号名还保持向后兼容,但信号编号在这些架构中不再处于未使用状态。
SIGURG
系统发送该信号给一个进程,表示套接字上存在带外(也称作紧急)数据(参见61.13.1节)。
SIGUSR1
该信号和SIGUSR2信号供程序员自定义使用。内核绝不会为进程产生这些信号。进程可以使用这些信号来相互通知事件的发生,或是彼此同步。在早期的UNIX实现中,这是可供应用随意使用的仅有的两个信号。(实际上,进程间可以相互发送任何信号,但如果内核也为进程产生了同类信号,这两种情况就有可能产生混淆。)现代UNIX实现则提供了很多实时信号,也可用于程序员自定义的目的(参见22.8节)。
SIGUSR2
参见对SIGUSR1信号的描述。
SIGVTALRM
调用setitimer()(参见23.1节)设置的虚拟定时器刚一到期,内核就会产生该信号。虚拟定时器计录的是进程在用户态所使用的CPU时间。
SIGWINCH
在窗口环境中,当终端窗口尺寸发生变化时(如62.9节所述,要么是由于用户手动调整了大小,要么是因为程序调用ioctl()对大小做了调整),会向前台进程组发送该信号。借助于为该信号安装的处理器程序,诸如vi和less之类的程序会在窗口尺寸调整后重新绘制输出。
SIGXCPU
当进程的CPU时间超出对应的资源限制时(参见36.3节对RLIMIT_CPU的描述),将发送此信号给进程。
SIGXFSZ
如果进程因试图增大文件(调用write()或truncate())而突破对进程文件大小的资源限制(参见36.3节对RLIMIT_FSIZE的描述)时,那么将发送此信号给进程。
表20-1总结了Linux下与信号相关的一系列信息。关于此表,请注意以下几点。
- 信号编号列所示为在不同硬件架构下对信号的编号。除非另有说明,信号在所有架构中编号相同。信号编号在架构上的差异会在括号中予以说明,所涉及的架构包括Sun SPARC、SPARC64 (S)、HP/Compaq/Digital Alpha (A)、MIPS (M)和HP PA-RISC (P)。此列中的undef表示此符号在所示架构中未定义。
- SUSv3列则表示SUSv3是否定义了该信号。
- 默认列显示了信号的默认行为。term表示信号终止进程,core表示进程产生核心转储文件并退出,ignore表示忽略该信号,stop表示信号停止了进程,cont表示信号恢复了一个已停止的进程。
某些前面列出的信号并未见诸于表 20-1,如 SIGCLD(SIGCHLD 信号的同义词)、SIGINFO(未使用)、SIGIOT(SIGABRT信号的同义词)、SIGLOST(未使用)和SIGUNUSED(在许多架构中是SIGSYS信号的同义词)。
| 名 称 | 信 号 值 | 描 述 | SUSv3 | 默认 | | :----- | :----- | :----- | :----- | :----- | :----- | :----- | | SIGABRT | 6 | 中止进程 | ● | core | | SIGALRM | 14 | 实时定时器过期 | ● | term | | SIGBUS | 7 (SAMP=10) | 内存访问错误 | ● | core | | SIGCHLD | 17 (SA=20, MP=18) | 终止或者停止子进程 | ● | ignore | | SIGCONT | 18 (SA=19, M=25, P=26) | 若停止则继续 | ● | cont | | SIGEMT | undef (SAMP=7) | 硬件错误 | | term | | SIGFPE | 8 | 算术异常 | ● | core | | SIGHUP | 1 | 挂起 | ● | term | | SIGILL | 4 | 非法指令 | ● | core | | SIGINT | 2 | 终端中断 | ● | term | | SIGIO / | 29 (SA=23, MP=22) | I/O时可能产生 | ● | term | | SIGPOLL | | SIGKILL | 9 | 必杀(确保杀死) | ● | term | | SIGPIPE | 13 | 管道断开 | ● | term | | SIGPROF | 27 (M=29, P=21) | 性能分析定时器过期 | ● | term | | SIGPWR | 30 (SA=29, MP=19) | 电量行将耗尽 | | term | | SIGQUIT | 3 | 终端退出 | ● | core | | SIGSEGV | 11 | 无效的内存引用 | ● | core | | SIGSTKFLT | 16 (SAM=undef, P=36) | 协处理器栈错误 | | term | | SIGSTOP | 19 (SA=17, M=23, P=24) | 确保停止 | ● | stop | | SIGSYS | 31 (SAMP=12) | 无效的系统调用 | ● | core | | SIGTERM | 15 | 终止进程 | ● | term | | SIGTRAP | 5 | 跟踪/断点陷阱 | ● | core | | SIGTSTP | 20 (SA=18, M=24, P=25) | 终端停止 | ● | stop | | SIGTTIN | 21 (M=26, P=27) | BG①从终端读取 | ● | stop | | SIGTTOU | 22 (M=27, P=28) | BG向终端写 | ● | stop | | SIGURG | 23 (SA=16, M=21, P=29) | 套接字上的紧急数据 | ● | ignore | | SIGUSR1 | 10 (SA=30, MP=16) | 用户自定义信号1 | ● | term | | SIGUSR2 | 12 (SA=31, MP=17) | 用户自定义信号2 | ● | term | | SIGVTALRM | 26 (M=28, P=20) | 虚拟定时器过期 | ● | term | | SIGWINCH | 28 (M=20, P=23) | 终端窗口尺寸发生变化 | | ignore | | SIGXCPU | 24 (M=30, P=33) | 突破对CPU时间的限制 | ● | core | | SIGXFSZ | 25 (M=31, P=34) | 突破对文件大小的限制 | ● | core |
针对表20-1中某些信号的默认行为,要注意以下几点。
- 在Linux 2.2中,信号SIGXCPU、SIGXFSZ、SIGSYS和SIGBUS的默认行为是终止进程,但不会产生核心转储文件。自内核2.4以后,Linux实现满足了SUSv3的要求,这些信号不但会引发进程终止,也将生成核心转储文件。在其他几个UNIX实现中,对信号SIGXCPU和SIGXFSZ的处理方式与Linux 2.2相同。
- 在其他的UNIX实现中,对SIGPWR信号的默认行为通常是将其忽略。
- 几个UNIX实现(特别是BSD衍生系统)默认情况下将忽略SIGIO信号。
- 虽然SIGEMT信号尚未获得任何标准的接纳,但却得到大多数UNIX实现的支持。然而,在其他实现中,该信号通常会导致进程终止并产生核心转储文件。
- SUSv1将SIGURG信号的默认行为定义为终止进程,这也是一些较老UNIX实现的默认做法。而SUSv2则采用了现行规范(将其忽略)。