标签:
信号就是软中断。
信号提供了异步处理事件的一种方式。例如,用户在终端按下结束进程键,使一个进程提前终止。
?
每一个信号都有一个名字,它们的名字都以SIG打头。例如,每当进程调用了abort函数时,都会产生一个SIGABRT信号。
每一个信号对应一个正整数,定义在头文件<signal.h>中。
没有信号对应整数0,kill函数使用信号编号0表示一种特殊情况,所以信号编号0又叫做空信号(null signal)。
下面的各种情况会产生一个信号:
对于进程来说,信号是随机产生的,所以进程不能简单地根据检测某个变量是否改变来判断信号是否发生,而应该告诉内核“当这个信号发生时,做下面的这些事情”。
我们告诉内核当某个信号发生时做的事情叫做信号处理函数。信号处理函数有三种功能可供选择:
?
对于一些信号发生时,会造成进程终止,同时生成一个core文件,该core文件记录了该进程终止时的内存情况,可以帮助调试和调查进程的终止状态。
有几种情况不会生成core文件:
函数声明
#include <signal.h>
void (*signal(int signo, void (*func)(int)))(int);
? ? Returns: previous disposition of signal if OK, SIG_ERR on err.
?函数声明解析:
void ? (*signal(int ? signr, ? void ? (*handler)(int)))(int);?
================================================?
handler是一个函数指针,指向参数为单参数int,返回类型void的函数?
signal是一个函数指针、这个函数指针指向一个参数为一个int型和一个handler型的指针、返回值是一个指向参数为int、返回值是void的函数的指针的指针。总结一下:?
? ? 这个复杂的声明可以用下面2种比较简单的型式表达出来,如下:?
第一种型式如下: ? ??
typedef ? void ? (*handler_pt)(int);?
handler_pt ? signal1(int ? signum,handler_pt ? ahandler);?
第二种型式如下:?
typedef ? void ? handler_t(int);?
handler_t* ? signal2(int ? signum, ? handler_t* ? ahandler);?
------------------------------------------------------?
以上这两种形式结果是等价的,但也有区别,第一种形式定义的是函数指针类型,?
sizeof(handler_pt)=4//borland ? c++ ? 5.6.4 ? for ? win ? 32,windos ? xp ? 32 ? platform?
第二种形式定义的是函数类型,如果对他使用sizeof(handler_t)会提示:?
sizeof ? may ? not ? be ? applied ? to ? a ? function
参数说明:
在上面的声明解析中我们可以看到,使用typedef可以简化signal函数的声明,后面对signal函数的调用也将使用简化后的声明:
typedef void Sigfunc(int);
Sigfunc *signal(int, Sigfunc *);
?
?Example
该例子的作用是捕获两个用户自定义的信号,并打印相关的信号信息。
使用函数pause来使程序挂起,知道接收到信号。
Code
#include "apue.h"
?
staticvoid sig_usr(int); ? /* one handler for both signals */
?? ? ? ? ?
int ? ? ?
main(void)
{ ? ? ? ?
? ? if (signal(SIGUSR1, sig_usr) == SIG_ERR)
? ? ? ? err_sys("can‘t catch SIGUSR1");
? ? if (signal(SIGUSR2, sig_usr) == SIG_ERR)
? ? ? ? err_sys("can‘t catch SIGUSR2");
? ? for ( ; ; )
? ? ? ? pause();
}? ?
?? ?
staticvoid
sig_usr(int signo)? ? ? /* argument is signal number */
{? ?
? ? if (signo == SIGUSR1)
? ? ? ? printf("received SIGUSR1\n");
? ? else if (signo == SIGUSR2)
? ? ? ? printf("received SIGUSR2\n");
? ??else
? ? ? ??err_dump("received signal %d\n", signo);
}
执行结果:
执行时,我们先让该程序后台执行,然后调用kill命令向该进程发送信号。
kill并不真的会杀死进程,而只是发送信号。所以kill并不是很准确的描述了该命令的作用。
当我们调用kill 2081命令时,进程被终止,因为在信号处理函数中并没有处理该信号,而该信号的默认处理程序为终止进程。
?
程序执行时,所有信号的状态都为默认值或者被忽略。
如果程序调用了exec系函数,则会改变信号的自定义处理函数为它的默认处理程序,因为在原来的程序中的处理函数地址对于新的程序来说是没有意义的。
例如,在一个交互式的shell中,启动一个后台进程,会设置该进程的中断和退出信号的处理动作为忽略,这样,当用户在shell中键入中断命令时,只会中断前台进程,而不会影响后台进程。
这个例子也告诉我们了signal函数的一个限制:我们无法确认当前进程的一些信号的处理动作,除非我们现在改变它们。后面我们将学习sigaction函数来确认一个信号的处理动作,而不需要改变它们。
?
当调用fork函数时,子进程继承了父进程的信号处理函数。因为子进程拷贝了父进程的内存,所以信号处理函数的地址对于子进程来说也是有意义的。
?
在早期的Unix系统中,信号是不可靠的。
不可靠的意思是,信号是有可能丢失的。即信号发生了,但是进程没有捕获它。
我们希望内核可以记住信号,当我们ready时,告诉我们该信号发生,让我们去处理。
早期的系统,对于信号机制的实现还有一个问题:当信号发生,执行了信号处理函数,该信号的处理函数就被置为默认的信号处理程序。因此,早期的关于信号的程序框架如下所示:
这段代码的问题在于,在SIGINT信号发生后,且在对它的信号处理函数重置为sig_int前,有一个时间差,在这个时间差内,可能再发生一次SIGINT信号。
如果第二次SIGINT发生在信号处理函数重置前,则会执行它的默认处理动作,即终止进程。
早期实现还有一个问题,就是如果进程不希望某个信号发生,它只能选择忽略它,而无法将该信号关闭。
一种使用场景是:我们不希望被信号打断,但是希望记住它们发生过。代码可能如下:
在这里,我们假设该信号只发生一次。
代码的目的在于:我们等待信号发生,信号发生之前,进程停止,等待。
代码的问题在于,有一个时间差,可能会发生异常情况,如果代码的执行序列如下:
1 信号发生
2 while (sig_int_flag == 0)
3 sig_int_flag= 1
4 pause()
这时,进程暂停挂起,等待信号发生,但是实际上该信号已经发生过了。这就导致了信号没有被捕获。
?
早期Unix操作系统的一个特性是:如果一个进程阻塞在一个“慢”系统调用,则该进程会收到一个信号,导致该进程被中断。该系统调用返回一个错误,并且errno设置为EINTR。
系统调用被分为两类:慢系统调用和其他系统调用。慢系统调用是那些可能永久阻塞的系统调用。慢系统调用包括:
对于可中断系统调用,我们需要在代码中处理errno EINTR:
为了避免需要显式处理可中断系统调用,一些可中断系统调用在发生阻塞时会自动重启。
这些会自动重启的可中断系统调用包括:ioctl, read, readv, write, writev, wait和waitepid。
如果某些应用并不希望这些系统调用自动重启,可以该系统调用单独设置SA_RESTART。
?
信号的发生导致程序的指令执行顺序被打乱。
但是在信号处理函数中,无法知道原进程的执行情况。
如果原进程这个在分配内存或者释放内存,或者调用了修改static变量的函数,并在信号处理函数中再次调用该函数,会发生不可预期的结果。
在信号处理函数中可以安全调用的函数称为可重入函数,也叫做异步信号安全的函数。除了保证可重入,这些函数还会阻塞可能导致结果不一致的信号。
如果函数满足下面的一种或者几种条件,则说明是不可重入的函数:
?
一直容易混淆的两个信号是SIGCLD和SIGCHLD。
SIGCLD来自System V,而SIGCHLD来自BSD和POSIX.1。
BSD SIGCHLD的语义:当该信号发生时,说明子进程的状态发生了改变,这时我们需要调用wait函数确认状态的变化。
对于System V系统中,对信号SIGCLD的处理说明如下:
?
?我们先定义几个信号相关的概念:
?可靠机制,不同的标准对于异常情况有不同的处理:
?
?参考资料:
《Advanced Programming in the UNIX Envinronment 3rd》
UNIX高级环境编程(13)信号 - 概念、signal函数、可重入函数
标签:
原文地址:http://www.cnblogs.com/suzhou/p/4488618.html