分类:
2013-09-09 14:15:27
原文地址:linux信号基本原理 作者:小林多喜三10
当一个进程收到信号后,用下列方式之一做出反应∶
1.忽略该信号;
2.捕获该信号(即,内核在继续执行该进程之前先运行一个由用户定义的函数);
3.让内核执行与该信号相关的默认动作。
现在用一个例子来简要说明信号的发送、捕获和处理,通过它,你就可以对信号有一个大致的印象。
例如,当某程序正在执行期间,如果发现它的运行有问题,我们可以用ctrl-c或delete键打断它的执行,这实际上就是向进程发送了一个中止信号。该进程收到这个中止信号后,可以根据事先的设定,对该信号做出相应的处理,如ctrl-c或delete键被定义为一个中止信号,进程接受到这个信号,便中途退出了。上面是用信号去中断另一个进程的实例。
除此以外,内核还可以通过发信号来通知一个进程:它的子进程已经终止,或通知一个超时进程:它已被设置警报(alarm)。
接下来我们开始详细介绍Linux系统中的一些与信号相关的函数。
1. 我们介绍的第一个函数是signal ()函数,它定义在ANSI
void * signal(int signum, void * handler);
它的第一个参数是将要处理的信号。第二参数是一个指针,该指针指向以下类型的涵数∶
void func();
当信号signum产生时,内核会尽快执行handler函数。一旦handler返回,内核便从中断点继续执行进程。第二参数可以取两个特殊值:SIG_IGN和SIG_DFL。SIG_IGN用以指出该信号应该被忽略;SIG_DFL用以指示,内核收到信号后将执行默认动作。尽管一个进程不能捕获SIGSTOP和SIGKILL信号,但是内核可以执行与该信号有关的默认动作作为替代,这些默认动作分别是暂停进程和终止进程。
2. 重发信号
当一个正在为SIGx运行handler的进程收到另一个SIGx信号时,它应该如何处理?通常,人们会希望内核中断该进程而再一次运行handler。为此,就要求无论何时调用handler,它都必须完全可用——即使当时她正在运行也必须如此。也就是说,要求handler必须是“可重入的(re - entrant)”。然而,设计可重入的handlers是件相当复杂的事情,因此, linux没有采用此种方案。
对于重发信号问题,起初的解决方案是:在执行用户定义的handler之前,重新将handler设置为SIG_DFL。然而,事实证明这是一个拙劣的解决方案,因为当两个信号迅速出现时,每个都给以不同地处理。内核为第一个信号执行handler而为第二个执行默认动作。这样,第二信号有时会导致该进程终止。因此,这个实现被称作"不可靠的信号( unreliable signals) "。
在下一节中,我们将看到POSIX signal API是怎样“漂亮地”解决这个难题的。
3. POSIX信号
POSIX signal API提供了一种新的机制,它能够处理多个信号而不必中断当前进程。
对于POSIX的信号实现来说,当一个进程正在处理一个信号时,如果有其他的信号到达,那末这些“其他的”信号将被挂起,直到该handler返回为止。可是,当一个SIGx信号在挂起之际,如果又发来另一个SIGx信号,那么内核仅把一个信号递送给该进程——也就是说,有一个信号丢失了。实际上这算不上是个大问题,因为信号除了信号号码本身之外不传送任何的信息。因此,在一个非常短的周期内多次发送一个信号相当于只将它发送一次。
信号集
POSIX signal函数(在
int sigemptyset(sigset_t * pset); -- 将pset中的信号全部清除
int sigfillset(sigset_t * pset); -- 将全部有效的信号填入pset
int sigaddset(sigset_t * pset, int signum); -- 将信号signum添加到pset
int sigdelset(sigset_t * pset, int signum); -- 从pset中删除信号signum。
int sigismember(const sigset_t * pset, int signum); -- 如果signum属于pset,返回一个非零值;否则为0。
记录Handler
为记录handler,需要调用sigaction()函数,原型如下∶
int sigaction(int signum, struct sigaction * act, struct sigaction * prev);
sigaction ()的作用是为信号signum设置handler。内核对signum的处理将在参数act中加以描述,sigaction类型如下:
struct sigaction {
sighanlder_t sa_hanlder; sigset_t sa_mask; unsigned long sa_flags; void (*sa_restorer)(void); /*从来不用* /};
sa_hanlder是一个指针,它指向以下类型的函数∶
void handler (int signum);
另外,它还有两个特值:SIG_DFL和SIG_IGN。sa_mask域包含了所有当handler运行时内核将阻塞的信号。注意,不管sa_mask的值为何,正在被处理的信号总是被阻塞。当然,通过适当设定sa_flags域的flags,你还是可以逾越这个特性的。通过逐位或运算,这些flags可以取下列一个或多个值:
1.SA_NOCLDSTOP - -确保父进程当它的一个子进程停止时不会收到一个SIGCHLD信号。
2.SA_NOMASK - -逾越该信号的默认阻塞,如果它的handler当前正在执行的话。这些flag能模拟ANSI的不可靠的信号。
3.SA_ONESHOT -重新设置signum的handler为SIG_DFL。这些flag模拟ANSI signal ()函数的行为
4.SA_RESTART - -当handler退出时,确保syscall重新启动。
如果sigactions的最后的参数不是NULL,在sigaction ()被调用之前用signum的序列填入。为了能够获得当前的信号序列而不改变它,应该把NULL作为第二个参数传递,然后将正确的sigaction指针作为第三个参数传递.
高级信号处理