技术开发 频道

Linux编程:信号篇


【IT168 技术文档】

第1节 信号

信号基本原理

    Linux是一种多用户多任务的操作系统,系统内会有多个进程存在。无论是操作系统与用户进程之间,还是用户进程之间,经常需要共享数据和交换信息。进程间相互通信的方法有多种,信号便是其中最为简单的一种,它用以指出某事件的发生。在Linux系统中,根据具体的的软硬件情况,内核程序会发出不同的信号来通知进程某个事件的发生。对于信号的发送,尽管可以由某些用户进程发出,但是大多数情况下,都是由内核程序在遇到以下几种特定情况的时候向进程发送的,例如:

1. 系统测出一个可能出现的硬件故障,如电源故障。

2. 程序出现异常行为,如企图使用该进程之外的存贮器。

3. 该进程的子进程已经终止。

4. 用户从终端向目标程序发出中断(BREAK)键、继续(CTRL-Q)键等。

    当一个信号正在被处理时,所有同样的信号都将暂时搁置(注意,并没有删除),直到这个信号处理完成后,才加以理会。

    当一个进程收到信号后,用下列方式之一做出反应∶

1.忽略该信号;

2.捕获该信号(即,内核在继续执行该进程之前先运行一个由用户定义的函数);

3.让内核执行与该信号相关的默认动作。

    现在用一个例子来简要说明信号的发送、捕获和处理,通过它,你就可以对信号有一个大致的印象。例如,当某程序正在执行期间,如果发现它的运行有问题,我们可以用ctrl-c或delete键打断它的执行,这实际上就是向进程发送了一个中止信号。该进程收到这个中止信号后,可以根据事先的设定,对该信号做出相应的处理,如ctrl-c或delete键被定义为一个中止信号,进程接受到这个信号,便中途退出了。上面是用信号去中断另一个进程的实例。除此以外,内核还可以通过发信号来通知一个进程:它的子进程已经终止,或通知一个超时进程:它已被设置警报(alarm)。

    接下来我们开始详细介绍Linux系统中的一些与信号相关的函数。

    我们介绍的第一个函数是signal ()函数,它定义在ANSI <signal.h>库中 ,该函数原型如下:

void * signal(int signum, void * handler);

    它的第一个参数是将要处理的信号。第二参数是一个指针,该指针指向以下类型的涵数∶

void func();

    当信号signum产生时,内核会尽快执行handler函数。一旦handler返回,内核便从中断点继续执行进程。第二参数可以取两个特殊值:SIG_IGN和SIG_DFL。SIG_IGN用以指出该信号应该被忽略;SIG_DFL用以指示,内核收到信号后将执行默认动作。尽管一个进程不能捕获SIGSTOP和SIGKILL信号,但是内核可以执行与该信号有关的默认动作作为替代,这些默认动作分别是暂停进程和终止进程。


重发信号

    当一个正在为SIGx运行handler的进程收到另一个SIGx信号时,它应该如何处理?通常,人们会希望内核中断该进程而再一次运行handler。为此,就要求无论何时调用handler,它都必须完全可用——即使当时她正在运行也必须如此。也就是说,要求handler必须是“可重入的(re - entrant)”。然而,设计可重入的handlers是件相当复杂的事情,因此, linux没有采用此种方案。

    对于重发信号问题,起初的解决方案是:在执行用户定义的handler之前,重新将handler设置为SIG_DFL。然而,事实证明这是一个拙劣的解决方案,因为当两个信号迅速出现时,每个都给以不同地处理。内核为第一个信号执行handler而为第二个执行默认动作。这样,第二信号有时会导致该进程终止。因此,这个实现被称作"不可靠的信号( unreliable signals) "。

    在下一节中,我们将看到POSIX signal API是怎样“漂亮地”解决这个难题的。

0
相关文章