10.2信号的概念
首先,每一个信号都有一个名字,这些名字都是使用相同的三个字母SIG 开始的。举例来说,SIGABRT是当进程调用数abort的时候生成的终止信号。SIGALRM是当函数alaram设置的定时时间到的时候生成的alarm信号。版本7已经有了15中信号;SVR4以及4.4BSD有31种不同的信号,FreBSD 8.0支持32中不同的信号:Mac OS X10.6.8以及Linux3.2.0各支持31中不同的信号;同时Solaris 10支持40中不同的信号。此外,FreeBSD,Linux以及Solaris为了支持实时应用还额外引入了应用定义的信号。但是在这本书中POSIX的实时扩展并不会进行讲解。
信号名称全部都是以正常数的形式定义在头文件<signal.h>
中。
不同的实现实际上是在不同的头文件中定义了信号,但是这些头文件都被头文件
<signal.h>
包含了,因为内核包含用于用户级应用的头文件被认为是一种糟糕的形式,所以在应用和内核二者都需要相同的定义的时候,这些信息被放在了内核文件中,然后再被用户级的头文件包含。FreeBSD8.0以及Mac OS X将信号定义在<sys/signal.h>
中,Linux3.2.0将其定义在头文件<bits/signum.h>中,Solaris 10将他们定义在文件
“`中。
没有信号数值为0的信号,我们将在10.9节中看到kill函数使用信号数值为0的信号作为特殊用途。POSIX.1将0称为null signal.
很多种情况下会产生信号:
- 终端产生的信号在用户按下特定终端按键的时候产生。按下终端上的DELETE按键(在很多系统上是Ctrl+C按键)通常会产生一个终端信号SIGINT,这就是用户停止一个正在运行进程的方法。(我们将在第18章中看到该信号如何映射到终端上的任意按键)。
- 硬件异常产生的信号:除以0,无效内存引用等等。这些条件通常被硬件检测到,然后通知内核。内核然后发出合适的信号并将其发送给在上述条件出现的时候正在运行的进程。举例来说,信号SIGSEGV是在执行一次无效内存引用的时候生成的。
- 函数
kill(2)
允许进程发送任意信号给另一个进程或者是进程组。当然在正常情况下还是存在限制的:我们必须是接收到我们发送信号的进程的拥有者,或者我们已经成为了超级用户。 - 命令kill(1)允许我们发送信号给其他进程或者是进程组,该程序只不过是kill函数的接口,该命令通常用来终端一个正在后台运行的进程
- 当一个进程应该被通知某些事件的时候,软件条件也能够产生信号,这些并不是硬件条件产生的信号(比如说除0条件),而是软件条件,实例有SIGURG是在out-of-band data arrives over a network connection.信号SIGPIPE是当进程输出数据到一个pipe上但是却没有进程读取的时候产生的,信号SIGALRM是在进程设置的时钟到时的时候产生的。
信号是异步时间的一个典型例子,它们可能在进程运行的任意时间点出现。因此进程不能简单地测试一个变量(比如说errno)去看是否有信号出现,取而代之的是,进程应该告诉内核:如果信号出现,那么按照指定流程执行就好了。
当信号出现的时候,我们可以告知内核做如下三件事中的一件,我们通常称之为信号的处置(disposition)或者是与信号相关联的动作(action):
- 忽略信号。对于许多信号都是这样做的,但是有连个信号不能忽略:SIGKILL以及SIGSTOP.这两个信号不能忽略的原因是:需要提供给内核以及超级用户杀死或者停止任意进程的一定成功的方法。同样的,如果我们忽略一些硬件异常(比如说非法内存访问或者是除0错误),那么进程的行为将会变成未定义的。
- 捕获信号。为了这样做,我们必须告诉内核在信号出现的时候调用我们指定的一个函数。在我们的函数中,我们可以按照我们的意愿来处理信号。如果我们正在写一个命令解释器,举例来说,当用户在键盘声点击生成一个中断信号的时候,我们可能想要返回到进程的主循环中去,终止我们正在为用户执行的任何命令。如果信号SIGCHLD被捕获到,这可能以为着一个子进程已经终止了,所以我们需要在信号捕捉函数中调用函数waitpid来获取终止子进程的进程ID以及终止状态。另外一个例子,如果进程已经创建了一个临时文件,我们可能需要些一个信号捕捉函数来捕捉信号SIGTERM(该终止信号是kill函数发出的默认信号)来清理临时文件。注意信号SIGKILL以及SIGSTOP是不可捕获的。
- 执行默认的处理。每一个信号都有一个默认的处理,具体的显示在图10.1中,注意许多信号的默认处理是终止进程。
图 10.1 列出了所有信号的名称,并使用一个标识指出哪些系统支持信号,以及该信号的默认处理方式,SUS列使用*表示是POSIX.1 specification的一部分,使用XSI表示其实XSI选项的一部分。
当默认处理方式标记为”terminate+core”的时候,表示进程的内存镜像被存储到该进程工作目录下的名称为core的文件中。该文件在许多UNIX系统的debuggers中用于查看进程终止时候的状态。
core文件的生成是许多UNIX系统的一个实现特性。虽然该特性不是POSIX.1标准的一部分。但是在Single Specification’s XSI option中被作为一个潜在的implementation-specific action提到。
core文件的名称与系统实现有关。在FreeBSD8.0中,core文件的名称是cmdname.core.其中cmdname是对应收到信号的进程的命令的名称。在Mac OS X 10.6.8中,core文件名称为core.pid,其中pid是收到信号的进程的ID,此外,这些系统上还允许core的文件名通过sysctl参数进行配置。在Linux3.2.0,该名称是通过/proc/sys/kernel/core_pattern.进行配置的。
许多系统将core文件放在对应进程的当前工作目录下;但是Mac OS X将所有的core文件放在目录/cores之下。
名称 | 描述 | ISO C | SUS | FreeBSD 8.0 | Linux3.2.0 | Mac OS X10.6.8 | Solaris 10 | 默认处理 |
---|---|---|---|---|---|---|---|---|
SIGABRT | 非正常终止(abort) | * | * | * | * | * | * | terminate+core |
SIGALRM | 定时时间到(alarm) | * | * | * | * | * | terminate | |
SIGBUS | 硬件错误 | * | * | * | * | * | terminate+core | |
SIGCANCEL | 线程库内部使用 | * | ignore | |||||
SIGCHILD | 子进程状态变化 | * | * | * | * | * | ignore | |
SIGCONT | 继续运行停止的进程 | * | * | * | * | * | continue/ignore | |
SIGEMT | 硬件错误 | * | * | * | * | terminate+core | ||
SIGFPE | 算术异常 | * | * | * | * | * | * | terminate+core |
SIGFREEZE | checkpoint freeze | * | ignore | |||||
SIGHUP | hangup | * | * | * | * | * | terminate | |
SIGILL | 非法指令(illegal) | * | * | * | * | * | * | terminate+core |
SIGINFO | 来自键盘的状态请求 | * | * | ignore | ||||
SIGINT | 终端中断字符 | * | * | * | * | * | * | terminate |
SIGIO | 异步IO | * | * | * | * | terminate/ignore | ||
SIGIOT | 硬件错误 | * | * | * | * | terminate+core | ||
SIGJVM1 | Java虚拟机内部使用 | * | ignore | |||||
SIGJVM2 | Java虚拟机内部使用 | * | ignore | |||||
SIGKILL | 终止运行 | * | * | * | * | * | terminate | |
SIGLOST | resource lost | * | terminate | |||||
SIGLWP | 线程库内部使用 | * | * | terminate/ignore | ||||
SIGPIPE | write to pipe with no readers | * | * | * | * | * | terminate | |
SIGPOLL | pollable event(poll) | * | * | terminate | ||||
SIGPROF | profiling time alarm(setitimer) | * | * | * | * | terminate | ||
SIGPWR | power fail/restart | * | terminate/ignore | |||||
SIGQUIT | 终端停止字符 | * | * | * | * | * | terminate+core | |
SIGSEGV | 无效内存引用 | * | * | * | * | * | * | terminate+core |
SIGSTKFLT | coprocessor stack fault | * | terminate | |||||
SIGSTOP | stop | * | * | * | * | * | stop process | |
SIGSYS | 无效系统调用 | XSI | * | * | * | * | terminate+core | |
SIGTERM | termination | * | * | * | * | * | * | terminate |
SIGTHAW | checkpoint thaw(解冻) | * | terminate | |||||
SIGTHR | 线程库内部使用 | * | terminate | |||||
SIGTRAP | 硬件错误 | XSI | * | * | * | * | terminate+core | |
SIGTSTP | terminal stop character | * | * | * | * | * | stop process | |
SIGTTIN | 后台进程从控制终端读取 | * | * | * | * | * | stop process | |
SIGTTOU | 后台进程写出到控制终端 | * | * | * | * | * | stop process | |
SIGURG | urgent condtion(sockets) | * | * | * | * | * | ignore | |
SIGUSR1 | 用户自定义信号 | * | * | * | * | * | terminate | |
SIGUSR2 | 用户自定义信号 | * | * | * | * | * | terminate | |
SIGVTALRM | virtual time alarm(setitimer) | XSI | * | * | * | * | terminate | |
SIGWAITING | 线程库内部使用 | * | ignore | |||||
SIGWINCH | 终端窗口尺寸变化 | * | * | * | * | ignore | ||
SIGXCPU | CPU limit exceeded(setrlimit) | XSI | * | * | * | * | terminate or terminate+core | |
SIGXFSZ | file size limit exceeded(setrlimit) | XSI | * | * | * | * | terminate or terminate+core | |
SIGXRES | resource control exceeded | * | ignore |
Figure 10.1 UNIX System signals
core文件在如下几种情况下并不会被生成:
- 进程是set-user-ID的,并且当前用户并不是程序文件的拥有者;
- 进程是set-group-ID的,并且当前用户并不是文件的组拥有者;
- 用户没有对当前工作目录执行写操作的权限;
- 文件已经存在,并且用户没有权限对其执行写操作;
- 文件太大了,(参考7.11中的RLIMIT_CORE);
core文件的权限通常是用户读权限、用户写权限的,但是Mac OS X将其设置为仅仅用户读权限。
在图10.1中,描述为”硬件错误”的信号对应实现定义的硬件错误,其中许多名称都是取自于UNIX系统的PDP-11实现。现在将对这些信号进行详细描述:
信号 | 描述 |
---|---|
SIABRT | 该信号通过调用函数abort产生(将在10.17中进行介绍),进程将非正常终止; |
SIGALRM | 该信号在函数alarm设置的定时器计时完成的时候生成(详见10.10节),在函数setitimer(2)中设置的间隔时间达到的时候也会生成该信 号。 |
SIGBUS | 该信号表示一个实现定义的硬件错误出现了,实现通常基于某种类型的内存错误产生的,这将在14.8节中详述 |
SIGCANCEL | 该信号是Solaris线程库内部使用的,没有什么通用的意义 |
SIGCHILD | 无论何时只要进程终止或者停止,信号SIGCHLD都会被发送到其父进程。默认情况下,该信号将被忽略,因此如果父进程想要在子进程的状态变化的时候通知到,那么就必须去捕获这一信号,通常情况下用于该信号捕获的函数是wait函数之一,用于抓取子进程的PID以及终止状态;;;;;;;System V的早期系统有一个相似的信号称为SIGCLD(没有字幕H),该信号的含义were different from those of other signals,并且早在SVR2中,手册就强烈建议用户不要在新程序中使用这一信号,(但是奇怪的是,该警告在SVR3以及SVR4版本的手册中消失了)。应用程序应该使用标准的SIGCHLD信号,但是我们必须清楚许多系统将SIGCLD与SIGCHLD定义成一样的,以保持兼容性。如果你再程序中继续使用SIGCLD的话,你就需要检查你的系统的手册,查看其实际对应的含义,我们将在10.7节中讨论这两个信号. |
SIGCONT | 当一个已经停止的进程被继续运行的时候就会发送一个作业控制信号给它,该信号的默认处理时继续运行一个已经停止的进程,但是如果进程并不处于停止状态,那么该信号就会被忽略。一个全屏编辑器,可能使用信号捕捉处理函数来捕捉该信号,实现终端屏幕的重绘。 |
SIGEMT | 这表示一个实现定义的硬件错误信号。名字EMT来源于PDP-11的”emulator trap”指令,并不是所有平台都支持该信号,比如说,在Linux上,SIGEMT仅仅在特定的架构上被支持,比如说SPARC,MIPS,以及PA-RISC. |
SIGFPE | 该信号是一个算术异常,比如说除零错误,浮点数溢出等等。 |
SIGFREEZE | 该信号仅仅在Solaris中被定义。该信号用于通知进程在冻结系统状态之前执行某些需要的操作,比如说在系统进入休眠或者暂停模式之前. |
SIGHUP | 该信号在终端接口检查到连接断开的时候被发送到控制进程(会话领导),参考图9.13中,我们可以看到该信号被发送到session的数据域s_leader指向的进程,该信号在终端的CLOCAL标记被清除的时候生成。(终端的CLOCAL标记在如下条件下被置位:the attached terminal is local.The flag tells the terminal driver to ignore all modem status lines.我们将在第18章中讲述该标志是如何设置的)。注意:收到该信号的会话领导可能是在后台运行的,这与常规的终端生成的信号(interrupt,quit,suspend)很不一样,这些信号总是发送给前台进程。信号SIGHUP在会话领导进程终止的时候也会终止,在这种情况下,该信号会被发送给所用运行在前台进程组中的进程。该信号通常用于通知守护进程(第13章中介绍):重新读取它们的配置文件。选择信号SIGHUP的原因是:守护进程并不会拥有终端,且在正常情况下并不会收到这一信号。 |
SIGILL | 该指令表示进程已经执行了一条非法指令。4.3BSD子啊abort函数中生成该信号,现在SIGABRT被用于此目的。 |
SIGINFO | 在BSD系统上,当我们键入一个状态键的时候(通常是Control-T),就会生成该信号,该信号将被发送到前台进程组的所有进程。该信号通常情况下会导致前台进程组的所有进程的状态信息被显示在终端上.Linux并不提供对SIGINFO信号的支持,虽然该标志在Alpha平台上定义的数值与SIGPWR一样,这可能是为了提供对OSF/1开发的程序的兼容性. |
SIGINT | 该信号在我们按下中断按键(通常是DELETE或者是Control-C按键)时由终端生成,并发送给所有前台进程组的进程,该信号通常用于终止失控的进程,尤其是某个进程在屏幕上输出了许多我们不想要的输出。 |
SIGIO | 该信号表示一个异步IO事件出现。我们将在14.5.2中进行讨论。在图10.1中我们将SIGIO的默认处理写为”terminate”或者”ignore”.不幸的是,这是与系统实现相关的,在System V上,SIGIO等同于SIGPOLL,因此其默认行为是终止进程,在BSD上,其默认行为是忽略该信号。Linux3.2.0以及Solaris 10将SIGIO的数值定义为与SIGPOLL一样,因此其默认行为也是终止进程,在FreeBSD 8.0以及Mac OS X 10.6.8上,其默认行为是忽略信号。 |
SIGIOT | 该信号代表一个实现定义的硬件错误.名称IOT源于PDP-11中指令”input/output TRAP”的助记符。在早期的System V中会在abort中生成该信号。但是现在使用信号SIGABRT予以替代。在FreeBSD 8.0,Linux 3.2.0,Mac OS X 10.6.8以及Solaris 10中,信号SIGIOT被定义为与SIGABRT相同的数值。 |
SIGJVM1 | 在Solaris上被Java虚拟机保留使用的信号。 |
SIGJVM2 | 另一个在Solaris上被Java虚拟机保留使用的信号。 |
SIGKILL | 该信号是两个不能够被俘获或者忽略的信号,该信号提供给了系统管理员一种确定OK的杀死任意进程的方法。 |
SIGLOST | 该信号用于通知运行在Solaris NFSv4 client system上的进程:某个处于恢复期间的锁不能被重新获取。 |
SIGLWP | 该信号是在Solaris线程库内部使用的信号,没有通用目的。在FreeBSD上SIGLWP被定义为SIGTHR的别名. |
SIGPIPE | 如果我们正在写数据到一个pipeline,但是reader已经终止了,在这种情况下就会产生这种信号,我们将在15.2中讲解pipes.当一个进程写数据到类型为SOCK_STREAM但是已经断开的socket的时候,也会生成这一信号。我们将在第16章中讲述sockets. |
SIGPOLL | 该信号在SUSv4中被标记为弃用的,所以在标准的未来版本中将被移除。当在一个pollable device上指定时间出现的时候被生成。我们将在14.4.2中将节poll函数的时候介绍这一信号。SIGPOLL源于SVR3, BSD中该信号在数值上等于SIGIO以及SIGURG信号。在Linux以及Solaris中,SIGPOLL的数值被定义为与SIGIO一致. |
SIGPROF | 该信号在SUSv4中被标记为弃用的,所以在未来版本的标准中该信号将被移除,当一个profiling interval timer set by the setitimer(2) functon expires的时候生成该信号. |
SIGPWR | 该信号与系统有关,主要用于带有不间断电源(uninterruptible power supply,UPS)供电的系统,如果常规电源出现问题,UPS将接管电源供应,软件将会接收到通知,当系统继续在电池供电状态下运行的时候并不需要做什么。但是当电池电量比较低的时候(比如说,电源在一段时间之后断电),那么软件将会再次收到通知,在这个时候,系统就应该关闭所有运行的进程以及系统,这也是信号SIGPWR发出的时候。在许多系统中,收到电源电量低之后会将信号SIGPWR发送给init进程,init进程来处理系统关机事宜。Solaris 10以及一些Linux发行版在文件inittab中有如下入口用于上述目的:powerfail,以及powerwait(或者powerokwait).在图10.1中,该信号的默认行为写的是”terminate”或者是”ignore”,不幸的是,其默认行为与系统有关,在Linux上的默认行为是终止进程,在Solaris上的默认行为是忽略信号. |
SIGQUIT | 该信号是在我们按下终端停止按键(通常是Control-backslash)的时候有终端驱动生成的。该信号将被发送到所有的前台进程组,该信号不仅仅终止前台进程组进程(正如信号SIGINT所做的那样),同时还会生成一个对应的core文件。 |
SIGSEGV | 该信号表示进程执行了一次无效内存引用,这通常是由于程序有bug导致的,比如说引用了一个未初始化的指针。名称SEGV代表”segmentation violation”. |
SIGSTKFLT | 该信号仅仅在Linux中有定义,出现在早期的Linux系统中,其原意是用于数字协处理器的堆栈错误。该信号在内核中并不会被产生,但是为了兼容性被保留下来了。 |
SIGSTOP | 作业控制信号用于停止进程。与交互式的停止信号(SIGTSTP)类似,但是信号SIGSTOP不能被捕获或者忽略。 |
SIGSYS | 该信号表示一次无效系统调用。有时候,进程执行了一条内核认为是系统调用的指令,但是该指令所带的参数表示该类系统调用是无效的。这可能在如下情况下发生:你在一个进程中使用了一个新的系统调用,但是却将其二进制文件在一个比较老的操作系统的上运行,在较老的操作系统上该系统调用并不存在。 |
SIGTERM | 默认情况下该信号是kill(1)命令发出的终止信号,因为它可以被程序捕获,捕获该信号可以给程序在终止之前一个优雅地清理的机会。(对比信号SIGKILL,SIGKILL信号是不能被捕获或者忽略的)。 |
SIGTHAW | 该信号仅仅在Solaris中定义,用于通知进程在系统被挂起以后再被恢复的时候需要采取一些特殊的操作。 |
SIGTHR | 该信号在FreeBSD上被线程库保留使用,其数值与SIGLWP一样。 |
SIGTRAP | 该信号表示实现定义的硬件错误。该信号名称来源于PDP-11的TRAP指令,实现经常使用此信号在断点指令被执行的时候将控制权传递给调试器。 |
SIGTSTP | 该交互停止信号在我们按下终端suspend key(通常是Control-Z)按键的时候由终端驱动生成,该信号被发送给所有前台进程组。不幸的是,stop名称已经有了别的含义,当讨论作业控制和信号的时候,我们提到stopping以及continuing作业。终端驱动在历史上使用名称stop来表示stopping以及starting终端输出,对应的字符分别是Control-S以及Control-Q字符。因此,终端驱动将生成的交互式停止信号称为suspend 字符,而不是stop字符。 |
SIGTTIN | 当后台进程组想要从控制终端读取内容的时候,终端驱动就会生成该信号,在特殊情况下,读取操作将会以errno被设置为EIO失败,条件有二:(a)读取进程被忽略或者阻塞了这个信号;(b)读取进程所在的进程组是孤儿进程组,那么该信号将不会生成。 |
SIGTTOU | 当一个后台进程想要输出数据到控制终端的时候,终端驱动就会发出这一信号,与后台进程读操作不一样的是,进程可以选择是否允许后台程序输出数据到控制终端,我们将在第18章中讲述如何修改这一选项。如果后台进程输出是不允许的,那么就像SIGTTIN一样,有如下两种特殊情况:(a).写进程被忽略或者阻塞了该信号;(b).输出数据的进程所在进程组是孤儿进程组,那么信号就不会被生成,取而代之的是,写操作返回一个错误,并且errno被设置为EIO.另外,无论后台程序输出是否被允许,特定的终端操作(除了写之外的)也可能生成SIGTTOU信号,这些操作包括tcsetattr,tcsendbreak,tcdrain,tcflush,tcflow以及tcsetpgrp,我们将在第18章中描述终端操作。 |
SIGURG | 该信号用于通知进程一个紧急条件出现,当out-of-band data在网络连接上被接受到,那么该信号是否产生是可选的。 |
SIGUSR1 | 这是用户自定义的信号,在应用程序中使用. |
SIGUSR2 | 这是另一个用户自定义的信号,在应用程序中使用. |
SIGVTALRM | 当函数**setitimer(2)设置的虚拟间隔定时器定时时间到的时候就会生成该信号. |
SIGWAITING | 该信号是Solaris线程库内部使用的信号,不可用于通用目的. |
SIGWINCH | 内核保存于每一个终端以及pseudo终端相关联的窗口尺寸,进程可以通过函数ioctl来获取或者设置窗口尺寸,我们将在18.12中将到这一点,如果一个进程修改了窗口尺寸,使得修改后的尺寸与上一次使用ioctl设置的窗口尺寸不同的时候。内核就会生成一个SIGWINCH信号。并发送给前台进程组。 |
SIGXCPU | 作为XSI选项的一部分,The Single UNIX Specification支持资源限制的概念,如果进程超过了其软件CPU时间限制,那么就会生成信号SIGXCPU.在图10.1中,我们记录的SIGXCPU的默认处理时”terminate”或者是”terminate with a core file”.其默认行为与系统有关,在Linux3.2.0以及Solaris 10中,其默认行为是终止并生成一个core文件,然而在FreeBSD8.0中以及Mac OS X中的默认处理方式是终止,但是并不会生成core文件,The Single UNIX Specification要求默认处理时非正常终止进程,但是是否生成一个core文件就留给了实现决定。 |
SIGXFSZ | 如果进程超过了其文件尺寸限制的话,就会生成这一信号。其默认行为的处理方式与SIGXCPU完全一致。 |
SIGXRES | 该信号仅仅被Solaris定义,可以选择当进程超过预配置的资源值的时候是否通知进程。在Solaris中,其资源控制机制是一个用于控制独立应用程序集合中共享资源使用的通用设备。 |