首先,每个信号都有一个名字。这些名字都以3个字符SIG开头。
例如:
- SIGABRT:夭折信号,当进程调用abort函数时产生这种信号。
- SIGALRM:闹钟信号,由alarm函数设置的定时器超时后将产生此信号。
V7 有 15 种不同的信号,SVR4 和4.4BSD 均有 31 种不同的信号。FreeBSD 8.0支持32种信号,Mac OS X 10.6.8以及Linux 3.2.0都支持31种信号,而So-laris 10支持40种信号。但是,FreeBSD、Linux和Solaris作为实时扩展都支持另外的应用程序定义的信号。虽然本书不包括POSIX实时扩展(有关信息请参阅Gallmeister[1995]),但是SUSv4已经把实时信号接口移至基础规范说明中。
在头文件<signal.h>
中,信号名都被定义为正整数常量(信号编号)。
实际上,实现将各信号定义在另一个头文件中,但是该头文件又包括在<signal.h>
中。
? : 内核包括对用户级应用程序有意义的头文件,这被认为是一种不好的形式,所以如若应用程序和内核两者都需使用同一定义,那么就将有关信息放置在内核头文件中,然后用户级头文件再包括该内核头文件。
于是,FreeBSD 8.0和Mac OS X 10.6.8将信号定义在<sys/signal.h>中,Linux3.2.0将信号定义在<bits/signum.h>中,Solaris 10将信号定义在<sys/iso/signal_iso.h>中。
不存在编号为 0 的信号。在 10.9 节中将会看到,kill 函数对信号编号 0 有特殊的应用。POSIX.1将此种信号编号值称为空信号。
信号的产生
很多条件可以产生信号。
- 当用户按某些终端键时,引发终端产生的信号。在终端上按
Delete
键(或者很多系统中的Ctrl+C
键)通常产生中断信号SIGINT
。这是停止一个已失去控制程序的方法。(第18章将说明此信号可被映射为终端上的任一字符。) - 硬件异常产生信号:除数为0、无效的内存引用等。这些条件通常由硬件检测到,并通知内核。然后内核为该条件发生时正在运行的进程产生适当的信号。例如,对执行一个无效内存引用的进程产生
SIGSEGV
信号。 - 进程调用kill(2)函数可将任意信号发送给另一个进程或进程组。自然,对此有所限制:接收信号进程和发送信号进程的所有者必须相同,或发送信号进程的所有者必须是超级用户。
- 用户可用kill(1)命令将信号发送给其他进程。此命令只是kill函数的接口。常用此命令终止一个失控的后台进程。
- 当检测到某种软件条件已经发生,并应将其通知有关进程时也产生信号。这里指的不是硬件产生条件(如除以 0),而是软件条件。例如
SIGURG
(在网络连接上传来带外的数据)、SIG-PIPE
(在管道的读进程已终止后,一个进程写此管道)以及SIGALRM
(进程所设置的定时器已经超时)。
信号是异步事件的经典实例。产生信号的事件对进程而言是随机出现的。进程不能简单地测试一个变量(如errno)来判断是否发生了一个信号,而是必须告诉内核“在此信号发生时,请执行下列操作”。
信号的处理
在某个信号出现时,可以告诉内核按下列3种方式之一进行处理,我们称之为信号的处理或与信号相关的动作。
- 忽略此信号。大多数信号都可使用这种方式进行处理,但有两种信号却决不能被忽略。它们是
SIGKILL
和SIGSTOP
。这两种信号不能被忽略的原因是:它们向内核和超级用户提供了使进程终止或停止的可靠方法。另外,如果忽略某些由硬件异常产生的信号(如非法内存引用或除以0),则进程的运行行为是未定义的。 - 捕捉信号。为了做到这一点,要通知内核在某种信号发生时,调用一个用户函数。在用户函数中,可执行用户希望对这种事件进行的处理。例如,若正在编写一个命令解释器,它将用户的输入解释为命令并执行之,当用户用键盘产生中断信号时,很可能希望该命令解释器返回到主循环,终止正在为该用户执行的命令。如果捕捉到
SIGCHLD
信号,则表示一个子进程已经终止,所以此信号的捕捉函数可以调用waitpid以取得该子进程的进程ID以及它的终止状态。又例如,如果进程创建了临时文件,那么可能要为SIGTERM
信号编写一个信号捕捉函数以清除临时文件(SIGTERM
是终止信号,kill
命令传送的系统默认信号是终止信号)。注意,不能捕捉SIGKILL
和SIGSTOP
信号。 - 执行系统默认动作。图10-1给出了对每一种信号的系统默认动作。注意,对大多数信号的系统默认动作是终止该进程。
图10-1列出了所有信号的名字,说明了哪些系统支持此信号以及对于这些信号的系统默认动作。在SUS 列中,“•”表示此种信号定义为基本POSIX.1 规范部分,“XSI”表示该信号定义在XSI扩展部分。在系统默认动作列,“终止+core”表示在进程当前工作目录的core文件中复制了该进程的内存映像(该文件名为core,由此可以看出这种功能很久之前就是UNIX的一部分)。大多数UNIX系统调试程序都使用core文件检查进程终止时的状态。
图10-1 UNIX系统信号
core文件
产生core文件是大多数UNIX系统的实现功能。虽然该功能不是POSIX.1的组成部分,但在Single UNIX Specification XSI的扩展部分中,这一功能作为一个潜在的特定实现的动作被提及。
在不同的实现中,core 文件的名字可能不同。例如,在FreeBSD 8.0 中,core 文件名为cmdname.core,其中cmd-name是接收到信号的进程所执行的命令名。在Mac OS X10.6.8中,core文件名是core.pid,其中,pid是接收到信号的进程的ID。(这些系统允许经sysctl参数配置core文件名。在Linux 3.2.0中,core文件名通过/proc/sys/kernel/core_pattern进行配置。)
大多数实现在相应进程的工作目录中包含core文件项;但Mac OS X将所有core文件都放置在/cores目录中。
在下列条件下不产生core文件:
?: 进程是设置用户ID的,而且当前用户并非程序文件的所有者;
? :进程是设置组ID的,而且当前用户并非该程序文件的组所有者;
- 用户没有写当前工作目录的权限;
- 文件已存在,而且用户对该文件设有写权限;
- 文件太大(回忆7.11节中的RLIMIT_CORE限制)。core文件的权限(假定该文件在此之前并不存在)通常是用户读/写,但Mac OS X只设置为用户读。
在图10-1说明中的“硬件故障”对应于实现定义的硬件故障。这些名字中有很多取自UNIX系统早先在PDP-11上的实现。请查看你所使用系统的手册,以确切地弄清楚这些信号对应于哪些错误类型。
信号的详细说明
SIGABRT
调用abort函数时(见10.17节)产生此信号。进程异常终止。SIGALRM
当用alarm函数设置的定时器超时时,产生此信号。详细情况见10.10节。若由setitimer(2)函数设置的间隔时间已经超时时,也产生此信号。SIGBUS
指示一个实现定义的硬件故障。当出现某些类型的内存故障时(如 14.8 节中说明的),实现常常产生此种信号。SIGCANCEL
这是Solaris线程库内部使用的信号。它不适用于一般应用。SIGCHLD
在一个进程终止或停止时,SIGCHLD
信号被送给其父进程。按系统默认,将忽略此信号。如果父进程希望被告知其子进程的这种状态改变,则应捕捉此信号。信号捕捉函数中通常要调用一种wait函数以取得子进程ID和其终止状态。System V的早期版本有一个名为SIGCLD(无H)的类似信号。这一信号具有与其他信号不同的语义,SVR2的手册页警告在新的程序中尽量不要使用这种信号。(令人奇怪的是,在SVR3和SVR4版的手册页中,该警告消失了。)应用程序应当使用标准的SIGCHLD
信号,但应了解,为了向后兼容,很多系统定义了与SIGCHLD
等同的SIGCLD
。如果有使用SIGCLD
的软件,需要查阅系统手册,了解它具体的语义。10.7节将讨论这两个信号。SIGCONT
此作业控制信号发送给需要继续运行,但当前处于停止状态的进程。如果接收到此信号的进程处于停止状态,则系统默认动作是使该进程继续运行;否则默认动作是忽略此信号。例如,全屏编辑程序在捕捉到此信号后,使用信号处理程序发出重新绘制终端屏幕的通知。关于进一步的情况见10.21节。SIGEMT
指示一个实现定义的硬件故障。EMT这一名字来自PDP-11的仿真器陷入(emulatortrap)指令。并非所有平台都支持此信号。例如,Linux只对SPARC、MIPS和PA_RISC等系统结构支持SIGEMT。SIGFPE
此信号表示一个算术运算异常,如除以0、浮点溢出等。SIGFREEZE
此信号仅由Solaris定义。它用于通知进程在冻结系统状态之前需要采取特定动作,例如当系统进入休眠或挂起状态时可能需要做这种处理。?:
SIGHUP
如果终端接口检测到一个连接断开,则将此信号送给与该终端相关的控制进程(会话首进程)。见图9-13,此信号被送给session结构中s_leader字段所指向的进程。仅当终端的CLOCAL
标志没有设置时,在上述条件下才产生此信号。(如果所连接的终端是本地的,则设置该终端的CLOCAL标志。它告诉终端驱动程序忽略所有调制解调器的状态行。第18章将说明如何设置此标志。)SIGILL
此信号表示进程已执行一条非法硬件指令。SIGINFO
这是一种BSD信号,当用户按状态键(一般采用Ctrl+T)时,终端驱动程序产生此信号并发送至前台进程组中的每一个进程(见图 9-9)。此信号通常造成在终端上显示前台进程组中各进程的状态信息。注意,接到此信号的会话首进程可能在后台,作为一个例子,请参见图9-7。这区别于由终端正常产生的几个信号(中断、退出和挂起),这些信号总是传递给前台进程组。如果会话首进程终止,也产生此信号。在这种情况,此信号送给前台进程组中的每一个进程。通常用此信号通知守护进程(见第13章)再次读取它们的配置文件。选用SIGHUP
的理由是,守护进程不会有控制终端,通常决不会接收到这种信号。4.3BSD的abort函数产生此信号。现在该函数产生SIGABRT
信号。虽然Alpha平台将SIGINFO
定义为与SIGPWR
具有相同值,但是Linux并不支持SIGINFO
信号。这更多是因为需要对OSF/1开发的软件提供某种程度的兼容。SIGINT
当用户按中断键(一般采用 Delete 或 Ctrl+C)时,终端驱动程序产生此信号并发送至前台进程组中的每一个进程(见图9-9)。当一个进程在运行时失控,特别是它正在屏幕上产生大量不需要的输出时,常用此信号终止它。SIGIO
此信号指示一个异步I/O事件。在14.5.2节中将对此进行讨论。在图10-1中,对SIGIO
的系统默认动作是终止或忽略。遗憾的是,这依赖于系统。在System V中,SIGIO
与SIGPOLL
相同,其默认动作是终止此进程。在BSD中,其默认动作是忽略此信号。Linux 3.2.0和Solaris 10将SIGIO
定义为与SIGPOLL
具有相同值,所以默认行为是终止该进程。在FreeBSD 8.0和MacOS X 10.6.8中,默认行为是忽略该信号。- SIGIOT 这指示一个实现定义的硬件故障。IOT这个名字来自于PDP-11,它是PDP-11计算机“输入/输出TRAP”(input/output TRAP)指令的缩写。System V的早期版本,由abort函数产生此信号。该函数现在产生
SIGABRT
信号。FreeBSD 8.0、Linux 3.2.0、Mac OS X 10.6.8和Solaris10将SIGIOT定义为与SIGABRT具相同值。 SIGJVM1
Solaris上为Java虚拟机预留的一个信号。SIGJVM2
Solaris上为Java虚拟机预留的另一个信号。SIGKILL
这是两个不能被捕捉或忽略信号中的一个。它向系统管理员提供了一种可以杀死任一进程的可靠方法。SIGLOST
运行在Solaris NFSv4客户端系统中的进程,恢复阶段不能重新获得锁,此时将由这个信号通知该进程。SIGLWP
此信号由Solaris线程库内部使用,并不做一般使用。在FreeBSD中,SIGLWP
是SIGTHR
的别名。SIGPIPE
如果在管道的读进程已终止时写管道,则产生此信号。15.2 节将说明管道。当类型为SOCK_STREAM
的套接字已不再连接时,进程写该套接字也产生此信号。我们将在第16章说明套接字。SIGPOLL
这个信号在SUSv4中已被标记为弃用,将来的标准可能会将此信号移除。当在一个可轮询设备上发生一个特定事件时产生此信号。14.4.2节将说明poll函数和此信号,它起源于SVR3,与BSD的SIGIO
和SIGURG
信号接近。在Linux和Solaris中,SIGPOLL
定义为与SIGIO
具有相同值。SIGPROF
这个信号在SUSv4中已被标记为弃用,将来的标准可能会将此信号移除。当setitimer(2)函数设置的梗概统计间隔定时器(profiling interval timer)已经超时时产生此信号。SIGPWR
这是一种依赖于系统的信号。它主要用于具有不间断电源(UPS)的系统。如果电源失效,则UPS起作用,而且通常软件会接到通知。在这种情况下,系统依靠蓄电池电源继续运行,所以无须做任何处理。但是如果蓄电池也将不能支持工作,则软件通常会再次接到通知,此时,系统必项使其各部分都停止运行。这时应当发送SIGPWR
信号。在大多数系统中,接到蓄电池电压过低信息的进程将信号SIGPWR
发送给init进程,然后由init处理停机操作。Solaris 10和有些Linux版本在inittab文件中有两个记录项用于此种目的:powerfail以及powerwait(或powerokwait)。在图10-1中,我们将SIGPWR
的默认动作标记为“终止或忽略”。遗憾的是,这种默认动作依赖于系统。Linux对此的默认动作是终止相关进程,而Solaris的默认动作是忽略该信号。SIGQUIT
当用户在终端上按退出键(一般采用Ctrl+\)时,中断驱动程序产生此信号,并发送给前台进程组中的所有进程(见图9-9)。此信号不仅终止前台进程组(如SIGINT
所做的那样),同时产生一个core文件。SIGSEGV
指示进程进行了一次无效的内存引用(通常说明程序有错,比如访问了一个未经初始化的指针)。名字SEGV代表“段违例”(segmentation violation)。SIGSTKFLT
此信号仅由Linux定义。它出现在Linux的早期版本,企图用于数学协处理器的栈故障。该信号并非由内核产生,但仍保留以向后兼容。SIGSTOP
这是一个作业控制信号,它停止一个进程。它类似于交互停止信号(SIGTSTP
),但是SIGSTOP
不能被捕捉或忽略。SIGSYS
该信号指示一个无效的系统调用。由于某种未知原因,进程执行了一条机器指令,内核认为这是一条系统调用,但该指令指示系统调用类型的参数却是无效的。这种情况是可能发生的,例如,若用户编写了一道使用新系统调用的程序,然后运行该程序的二进制可执行代码,而所用的操作系统却是不支持该系统调用的较早版本,于是就出现上述情况。SIGTERM
这是由kill(1)命令发送的系统默认终止信号。由于该信号是由应用程序捕获的,使用SIGTERM
也让程序有机会在退出之前做好清理工作,从而优雅地终止(相对于SIGKILL
而言。SIGKILL
不能被捕捉或者忽略)。SIGTHAW
此信号仅由Solaris定义。在被挂起的系统恢复时,该信号用于通知相关进程,它们需要采取特定的动作。SIGTHR
FreeBSD线程库预留的信号,它的值定义或与SIGLWP相同。SIGTRAP
指示一个实现定义的硬件故障。此信号名来自于PDP-11的TRAP指令。当执行断点指令时,实现常用此信号将控制转移至调试程序。SIGTSTP
交互停止信号,当用户在终端上按挂起键(一般采用 Ctrl+Z)时,终端驱动程序产生此信号。该信号发送至前台进程组中的所有进程(参见图9-9)。遗憾的是,停止具有不同的含义。当讨论作业控制和信号时,我们谈及停止和继续作业。但是,终端驱动程序一直使用术语“停止”表示用Ctrl+S字符终止终端输出,为了继续启动该终端输出,则用Ctrl+Q字符。为此,终端驱动程序称产生交互停止信号的字符为挂起字符,而非停止字符。SIGTTIN
当一个后台进程组进程试图读其控制终端时,终端驱动程序产生此信号(见9.8节中对此问题的讨论)。在下列例外情形下不产生此信号:(a)读进程忽略或阻塞此信号;(b)读进程所属的进程组是孤儿进程组,此时读操作返回出错,errno设置为EIO。SIGTTOU
当一个后台进程组进程试图写其控制终端时,终端驱动程序产生此信号(见9.8节对此问题的讨论)。与上面所述的SIGTTIN
信号不同,一个进程可以选择允许后台进程写控制终端。第18章将讨论如何更改此选项。如果不允许后台进程写,则与SIGTTIN
相似,也有两种特殊情况:(a)写进程忽略或阻塞此信号;(b)写进程所属进程组是孤儿进程组。在第2种情况下不产生此信号,写操作返回出错,errno设置为EIO。不论是否允许后台进程写,一些除写以外的下列终端操作也能产生SIGTTOU
信号,如tcsetattr、tcsendbreak、tcdrain、tcflush、tcflow以及tcsetpgrp。第18章将说明这些终端操作。SIGURG
此信号通知进程已经发生一个紧急情况。在网络连接上接到带外的数据时,可选择地产生此信号。SIGUSR1
这是一个用户定义的信号,可用于应用程序。SIGUSR2
这是另一个用户定义的信号,与SIGUSR1
相似,可用于应用程序。SIGVTALRM
当一个由setitimer(2)函数设置的虚拟间隔时间已经超时时,产生此信号。SIGWAITING
此信号由Solaris线程库内部使用,不做他用。SIGWINCH
内核维持与每个终端或伪终端相关联窗口的大小。进程可以用ioctl函数(见18.12 节)得到或设置窗口的大小。如果进程用 ioctl 的设置窗口大小命令更改了窗口大小,则内核将SIGWINCH
信号发送至前台进程组。SIGXCPU
Single UNIX Specification的XSI扩展支持资源限制的概念(见7.11节)。如果进程超过了其软CPU时间限制,则产生此信号。在图10-1中,对于SIGXCPU
的默认动作说明为“终止或终止+core”。该默认动作依赖于操作系统。Linux 3.2.0和Solaris 10支持的默认动作是终止并创建core文件;FreeBSD 8.0和Mac OS X 10.6.8支持的默认动作是终止且不产生core文件。Single UNIX Specification要求该默认动作是,异常终止该进程,是否创建core文件则留给实现决定。SIGXFSZ
如果进程超过了其软文件长度限制(见7.11节),则产生此信号。如同SIGXCPU一样,针对SIGXFSZ的默认动作依赖于操作系统。Linux 3.2.0和Solaris 10对此信号的默认动作是终止并创建core文件。FreeBSD 8.0和Mac OS X 10.6.8支持的默认动作是终止且不产生core文件。Single UNIX Specification要求该默认动作是异常终止该进程,是否创建core文件则留给实现决定。SIGXRES
此信号仅由Solaris定义。可选择地使用此信号以通知进程超过了预配置的资源值。Solaris资源限制机制是一种通用设施,用于控制在独立应用集之间共享资源的使用。