14.11 信号处理signal.h

所谓信号,多半是指程序运行期间发生的一些异常情况,比如出现了除以0这样的错误。有的信号是外部传入的,比如用户按下了Ctrl-C组合键要求程序暂停或终止。也有那种没事找事由程序自己刻意制造并发出的信号。

也就是说,信号是某种状态变化或事件发生的信息。但重要的是这种信息能被计算机捕获并通知给运行的程序。

就一般意义而言,没有人能预先料到何时会产生这种信号。但是这种信号往往又要求程序必须立即进行必要的处理。这就是“signal.h”中的内容要解决的问题。

“signal.h”中首先定义了若干宏来分别表示各种信号,这些宏都以“SIG”开头,

SIGABRT:非正常终止信号,调用abort()函数就可以产生这个信号。

SIGFPE:数学运算错误信号,比如发生了除以0的时候可以产生这个信号。

SIGILL:非法指令信号。

SIGINT:收到交互请求。

SIGSEGV:非法访问内存。

SIGTERM:终止请求信号。

在产生信号后,程序立刻暂停正在进行的工作(但保留现场)去执行另外一个函数,返回后继续原先暂停的工作。这就是程序进行信号处理的机制。

那个被立刻执行的函数一般叫信号处理器。要完成这个工作,必要的准备是事先登记发生何种信号时用哪个信号处理器处理,因为这种函数通常不可能通过代码中显式的函数调用来执行。函数“signal()”的功能就是进行这种登记工作。它的函数原型是

14.11 信号处理signal.h - 图1

这个原型让人看起来有些晕,实际上却没那么复杂。

这个说明描述的是“signal”,显然这是一个函数的名字。

“()”内的“int sig, void(*func)(int)”是这个函数参数及类型。第一个参数是“hit”类型。对应的实参就是需要处理的信号。实参的值就是前面提到的那些符号常量或者说宏。

第二个参数是指向函数类型的指针,所指向的函数的参数为“hit”类型,返回值为“void”类型。实际上这个函数的类型就是进行信号处理的函数的类型,即“信号处理器”的类型。

对应于第二个参数的实参就是所谓的信号处理器。这就是“signal()”函数要登记的函数。

“signal()”函数返回指向一个以“int”为参数返回值为“void”类型的函数的指针,实际上也是“信号处理器”的类型。大多数情况下登记了什么函数就返回指向这个函数的指针。

总之,“signal()”函数的功能无非是在说什么样的信号(第一个参数)用什么函数(第二个参数)处理。如果登记成功就依然返回指向这个处理函数的指针。

这个处理器处理函数可能是自己编写的,也可能是库提供的。库提供的两个可能的参数如下所示。

SIG_DFL:这个宏表示按照默认方式处理信号。

SIG_IGN:这个宏表示某种信号被忽略,不处理。

例如

14.11 信号处理signal.h - 图2

表示的是“SIGINT”,这种信号应当被忽略。

函数调用的返回值一般被用来记录处理器函数,以便再次登记这个函数。如果登记信号处理函数不成功,“signal()”函数返回的是“SIG_ERR”,这也是“signal.h”中定义的一个宏。

也可以自己定义这种信号处理函数。定义时请注意这种函数的类型一律是“void f(int)”。

“signal.h”中描述的另一个函数是

14.11 信号处理signal.h - 图3

这个函数用于由程序人为地发出一个信号,显然其参数就是前面提到的描述各种参数的宏。返回值表示信号是否成功发出,0表示成功,非0表示不成功。

“signal.h”中还定义了一种数据类型,如下所示。

sig_atomic_t:这是一种很小的整数类型,其值的范围在“stdint.h”中描述。标准保证在异步中断时,这种类型会在一条指令内被操作。