我们介绍一些标准信号的名称以及它们代表的事件。每一个信号名称是一个代表正整数的宏,但是你不要试图去推测宏代表的具体数值,而是直接使用名称。这是因为这个数值会随不同的系统或同样系统的不同版本而不同,但是名称还算是标准化和统一的。
这些名称定义在signal.h中。
int NSIG是一个定义的宏,它描述了定义的信号的数量。由于信号的数值是从0开始连续分配的,所以,NSIG比系统中所定义的最大的信号数值大1。
11.2.1 程序出错信号
下面介绍的信号是有程序的错误造成的。这些严重的错误会被计算机或操作系统检测出来。一般情况下,产生了这种信号表明你的程序遭到严重的破坏,没有办法继续完成产生错误的计算。
很多程序要控制这些信号是要在程序退出前进行一些清理工作。比如,关闭临时文件,清理缓冲区等。程序可以注册一个函数句柄来完成这些工作,然后在让系统执行缺省的操作来结束进程的执行。终止可能是产生这类错误的程序的最后的操作,但也有例外,比如一些在解释环境中执行的程序,产生错误时需要返回解释环境本身。
这类信号的缺省动作就是终止程序的执行。如果你定义了自己的处理,而没有最后调用终止进程,或者阻塞或忽略了该信号,那么你的程序可能会造成极其严重的后果。除非这个信号本身并不是出错产生的,而是通过调用kill函数或raise函数直接通过系统发送的。
当这些信号结束进程时,系统会产生一个内核转储文件,用来记录发生错误退出前程序的状态。内核转储文件的文件名是core,它会被写到当前进程的当前目录中。在Linux系统中,你也可以通过环境变量COREFILE来设定产生内核转储文件的文件名。产生内核转储文件的目的是帮助你事后利用调试器查明产生错误的原因。
下面逐一介绍这类信号的名称:
int SIGFPE
这个信号表明产生了一个致命的算术运算错误。尽管该信号的名称来源于“浮点异常(floating-point exception)”,但是,它实际上包含了所有的算术指令错误,包括除数是0和溢出等。
int SIGILL
这个信号的名称来源于“非法的指令(illegal instrution)”,它往往意味着你的程序要执行根本无法译码的指令或你无权执行的特权指令。既然C语言只能编译产生有效的合法的指令,所以 SIGILL信号很多情况下表明可执行文件破坏了,或者正在把数据当作代码来执行。后一种情况往往是由于把一个指针错当成函数指针传递,或者数组越界破坏了堆栈段的数据,使其中的函数返回地址错误等。SIGILL也可以由堆栈溢出,或者系统无法执行传递给系统的信号响应函数的句柄引起。
int SIGSEGV
当程序试图读或写系统分配给它的内存以外的存储器,或者写只有读权限的存储器时会发生这个信号。实际上,由于操作系统检测机制的限制,对程序的检测并不是那么及时,往往只有当超出范围很远时系统才会检测出来。信号的名称来源于“段变异(segmentation violation)”。这个信号的发生往往是由于引用没有初始化或空的指针,或者用指针引用数组时由于疏于检查而越界。
int SIGBUS
这个信号由于引用无效的指针产生。和SIGSEGV相似,典型情况下,它也是由于没有正确初始化指针引起的。它们的区别是,SIGSEGV是用无效的指针引用了有效的内存地址,而SIGBUS是引用了无效的内存地址。SIGBUS通常的产生是因为没有正确初始化指针变量,比如,指针指向8字节对齐的变量而引用的地址是奇数。该信号的名称是“总线错误(bus error)”的缩写。
int SIGABRT
这个信号是程序调用函数abort()产生的。关于函数abort()的用法,参见前面的有关章节。
int SIGIOT
在Linux系统中,它是SIGABRT的另一个名称。
int SIGTRAP
该信号是由计算机的断点指令产生的。调试程序使用该信号。当程序执行到设置断点的指令,引起发送该信号,同时调试程序捕获该信号,获得控制权,进行程序的调试。因此,你的程序是无法看到该信号的。
int SIGEMT
模拟自陷信号。它是由系统未能实现,必须由软件模拟的指令引起的。截获该信号,并在软件中模拟引起信号的指令的执行。
int SIGSYS
错误的系统调用。就是说,程序进行了系统调用,但是传递给系统的调用号是错误的,系统无法完成调用。
11.2.2 程序终止信号
这些信号都是用来告诉程序通过某种方式结束。它们之所以有各种不同的名称,是因为它们使用的目的稍有不同,或者程序希望用稍微不同的方式处理它们。虽然这些信号对程序的后果都是相同的,都是结束程序的执行,但还是有理由处理这些信号。通常是因为程序希望在结束之前能够清理一下,比如,能够纪录下某中状态,等等。
缺省的处理(目前)是结束进程的执行。
int SIGTERM
该信号是一个最普通的让程序结束的信号。进程可以阻塞、控制和忽略该信号,它是礼貌的要求一个程序结束的普通的方法,比如,通过shell命令kill在缺省情况下就发送该信号结束进程。
int SIGINT
该名称是program interrupt的缩写,当用户从控制台输入终止字符(通常是Ctrl-c)时发送给进程的信号。
int SIGQUIT
该信号和SIGINT相似,区别是它通过用户输入退出字符(通常是Ctrl-/)来产生,进程处理退出外,还要产生内核转储,就象接收到了错误信号一样。对于这个信号,你可以认为是用户发现了程序错误而通知程序的一种方法。在处理该信号时,某些退出清理最好不要做,这样能够让用户可以通过转储的内核来察看当时的状态。
int SIGKILL
SIGKILL信号用来让进程立即终止,它不能被控制和忽略,总是致命的,也不能阻塞。该信号通常只能通过特定的命令直接产生,既然它是不可忽略的,你应该把它作为最后的手段使用,而首先使用不太激烈的手段,如Ctrl-c或者SIGTERM等方法。如果一个进程对其它结束信号没有响应,则采用 SIGKILL信号,总是能让程序结束。事实上,如果SIGKILL信号也不起作用的话,你就应该报告一个内核错误。当一个进程由于某中原因不能在进行下去的时候,系统也能够发送该消息结束进程的执行。
int SIGHUP
SIGHUP信号(hang up)报告用户的终端已经从系统中断开,或者用来作为作业控制的手段。
11.2.3 闹钟信号
这类信号用来表明定时器激活,信号的缺省行为是让进程结束。虽然这种缺省行为通常是没有什么用处的,但是没有其它的缺省行为可用。因此,往往需要程序用自己的函数控制该类信号的行为。
int SIGALRM
该信号被使用实际时间或时钟数的定时器使用,比如,alarm()函数。
int SIGVTALRM
该函数被使用当前进程使用的CPU时间的函数使用。
int SIGPROF
该信号用来表明当前进程使用的CPU时间和为当前进程服务的系统消耗的CPU时间的综合,一般用来生成代码的简略概括。
11.2.4 异步I/O信号
这类信号和异步I/O操作有关,你必须通过直接调用fcntl()函数才能使某些文件描述符产生该信号。该信号的缺省动作是忽略该信号的作用。
int SIGIO
SIGIO信号是当某个打开的文件描述符已经准备好输入输出时才会发送该信号。在许多系统上,只有终端和套接口才可能发送这个信号,而普通的文件不会发送这个信号。在GNU系统上,任何文件,只要你说明它是异步打开,就可能发送这个信号。
int SIGURG
这个信号只用来表示套接口接受到紧急或者带外数据时使用,参看后面的网络编程的部分。
int SIGPOLL
系统v的信号,和SIGIO相似,只是为了兼容的目的才设立。
11.2.5 作业控制信号
这一组信号用来支持作业控制,一般情况下,你可以不用管这些信号,采用系统缺省的行为就可以了,除非你确切知道该怎样控制作业系统的工作。
int SIGCHLD
当一个子进程结束时,就会向父进程发送该信号。该信号的缺省行为是忽略,当进程建立起一个处理该信号的函数句柄,并且这是已经有zombie进程存在,那么是否产生该信号由系统决定。
int SIGCLD
信号SIGCHLD的旧名称。
int SIGCONT
这个信号的作用是当进程被停止后使进程继续执行,而不做任何其他的工作。你不能阻塞该信号,但是你可以为该信号设置一个处理函数句柄,它总是使进程无条件的执行下去。大多数程序没有理由控制该信号,它们只是简单的继续被中断的操作。你可以利用该处理函数的句柄完成一些你需要的特殊工作,比如,重新打印一些提示信息,如果程序是因为等待输入而被挂起的话。
int SIGSTOP
该信号停止一个进程,它不能被控制、忽略和阻塞。
int SIGTSTP
这是一个交互式的停止信号。不象SIGSTOP,它可以被控制或忽略。当你从控制台输入SUSP字符(Ctrl-z)时,就会产生该信号。
int SIGTTIN
当一个进程在后台执行时,它不能从用户的终端中读入任何输入。当一个后台进程试图从用户终端读时,所有进程都会接受到一个SIGTTIN信号。该信号的缺省操作是停止进程,从而让需要输入的后台进程能够到前台读入需要的输入信息。
int SIGTTOU
它和SIGTTIN相似,只是发生在后台进程写终端时。
当一个进程停止时,任何信号都不会再传递给它,除了SIGKILL和SIGCONT信号(显然),所有给它的信号标记为未定的(pending),并且只有当进程重新进入执行状态时才得到最终传递。SIGKILL信号总是迫使进程结束,而且不能阻塞、忽略和控制。你可以忽略SIGCONT信号,但是它总是能够使被停止的进程继续执行。一个SIGCONT信号可以让所有未定的停止信号被丢弃,类似的,一个停止信号能让未定的SIGCONT信号丢弃。
11.2.6 操作错误信号
这组信号都是由进程的操作错误引起的,他们不必是程序的错误,而是任何阻止操作完成的错误。这些信号的缺省操作是中止进程的执行。
int SIGPIPE
如果你使用管道或者FIFO进行进程间的通讯,你必须让你的应用程序在写入一个管道之前先有一个程序已经打开了该管道并且已经开始读取数据。如果这时没有开始,或者读管道的进程意外退出了,那么写操作就会产生一个SIGPIPE信号。如果SIGPIPE信号被阻塞、控制或忽略,那么写操作将返回错误,错误代码是EPIPE。更进一步的信息参见后面的相关章节。
int SIGLOST
该信号表明资源的丢失,在GNU系统中,通常任何提供服务的服务器意外死机,都能引起该信号。一般忽略该信号并无不妥,因为和这种操作相关的错误都能使相关函数返回错误。
int SIGXCPU
CPU时间限制到。该信号表明对进程使用的CPU时间的限制已经达到。
int SIGXFSZ
该信号表明进程试图增长文件超过系统对文件长度的限制。
11.2.7 外围信号
这组信号用于各种各样的目的,一般不会影响进程的执行。
int SIGUSR1
int SIGUSR2
这两个信号可以用来完成你希望的任何目的,通常用来进行网络通讯。如果你在一个进程里有用来接受该信号的程序,你的另外的进程就可以发送信号给相应的进程。这个信号的缺省操作是终止进程的执行。
int SIGWINCH
系统终端的每屏行数和列数发生改变时发送该信号。它的缺省操作是忽略。如果是一个全屏幕输出的程序则需要控制该信号,根据新的每屏行和列数重新初始化输出。
int SIGINFO
该信号可以由控制台通过键盘发送给所有前台进程组的进程。如果接收信号的进程是领头进程,那么它一般会打印一些系统信息和进程的一些当前信息,如果是其他进程,那么缺省情况下不会做任何事情。
11.2.7 信号消息
我们上面提到的标准信号都可以用一个系统提供的字符串描述。我们用函数strsignal()和psignal()来获取相关的字符串。
char * strsignal(int signum)
该函数返回一个已经分配好的静态字符串,用来描述signum信号相关的文字信息。你无权修改这个返回的字符串,并且,既然在另外的调用中能够重写该字符串,如果你需要在后面的程序中使用,必须自己保存该字符串的备份。该函数是GNU系统的扩充,它的原型包含在string.h头文件中。
void psignal(int signum, const char *message)
该函数输出一个描述signum信号的消息到标准错误输出。如果传递该函数的message是一个NULL指针或空串,这个函数只打印和信号相关的标准的消息。如果你传递给函数非空的message参数,那么系统会先输出message字符串,然后输出相关消息。该函数在signal.h文件中声明。