标签:
之前看APUE上面信号一章的时候,看到APUE上面说早期的signal函数实现让安装的信号处理函数只能使用一次,当第二次再收到该信号的时候,进程将会执行该信号的默认动作,之前安装的信号处理函数将失效。于是我就去做实验验证一下,代码如下:
1 #include <stdio.h> 2 #include <signal.h> 3 4 static void sigint_act(int); 5 6 int main(int argc, char *argv[]) 7 { 8 signal(SIGINT, sigint_act); 9 // sysv_signal(SIGINT, sigint_act); 10 11 while (1); 12 13 return 0; 14 } 15 16 17 static void sigint_act(int signo) 18 { 19 printf("accept SIGINT signal succeed!\n"); 20 21 sleep(3); 22 23 printf("finished!\n"); 24 }
编译上面的代码后,执行的结果如下图所示:
仔细分析这个结果就知道和APUE上面提到的结论是不符合的;如果按照APUE上面说的,那么在第二次按下ctrl+c键的时候应该去执行SIGINT信号的默认动作,而SIGINT信号的默认动作是终止进程,所以进程应该会终止,但是从实验的结果来看进程并没有终止,这怎么解释?
接着上网去找资料,看了一篇博客(http://blog.chinaunix.net/uid-24774106-id-4061386.html)后,算是能解释上面的实验结果。博客上面说的原因是现代的signal函数和早期的signal函数并不相同,现代的signal函数安装的信号处理函数可以永久使用。那这就可以解释上面的实验结果中为什么进程没有终止。博客中还提到了sysv_signal函数,这个函数和早期的signal函数的实现是相同的,我用sysv_signal函数做了个实验,发现第二次按下ctrl+c按键时进程确实是立即终止了,如下图所示,证明博客中的内容是正确的。
上面实验结果中虽然解释了为什么进程没有终止,但是为什么会执行两次信号处理函数?博客中的内容也能解释这一原因,早期的signal函数和现代的signal函数还有一点区别就是早期的signal函数在执行某一信号的信号处理函数期间不会屏蔽阻塞该信号,而现代的signal函数会阻塞屏蔽该信号,等待信号处理函数执行完后再去处理该信号。(上面做的sysv_signal函数的实验实际上已经验证了这一点,在执行信号处理函数期间按下了ctrl+c键,进程便立即终止了,说明进程并没有阻塞SIGINT信号,因为如果阻塞了该信号,那么应该会等到信号处理函数执行完毕后,再终止进程);第一次实验的实验结果之所以会执行两遍信号处理函数,是因为按下第二次ctrl+c键的时候正在执行信号处理函数,进程会阻塞该信号,但是仍然收到了该信号,待执行完了信号处理函数后(打印出了finished后)由于进程仍然接收到了信号,所以进程又去执行一遍信号处理函数。于是我又去做了个实验,如下图所示:
当正在执行信号处理函数期间,我又按了6次ctrl+c键,我的想法是会执行6次函数,但是实验结果不遂我愿只执行了两次函数,于是又去网上找资料,找到了一篇博客(http://blog.sina.com.cn/s/blog_7a9cae0101010hth.html),解决了我的这个疑问。
在博客中提到了信号的“未决状态”和“阻塞状态”,“未决状态”是指进程已经接收到该信号,不管进程处不处理该信号,只是标志着进程已经收到了该信号;“阻塞状态”指该进程在收到该信号后不去响应信号(意思就是不去执行信号的信号处理函数,不管是忽略、默认动作还是安装的函数)。上面的实验中当第二次按下ctrl+c键的时候产生的SIGINT信号处于未决状态,由于在执行信号处理函数期间会阻塞该信号,所以会等到函数执行完毕后再去执行信号处理函数。
还有一个不可靠信号和可靠信号之分,信号编号在32之前的信号都是不可靠信号,在32之后的都是可靠信号;不可靠信号是指不能排队的信号,信号可能会出现丢弃,可靠信号是指可以排队的信号。按照这个定义,那么SIGINT信号应该是属于不可靠信号(编号小于32),那么为什么上面的实验中好像信号出现了排队的现象,在按下第二次ctrl+c键的时候不应该丢弃的嘛?要解释这个问题,需要去了解信号的生命周期,同时上面的疑问(按6次键)也可以解释了。
信号的生命周期可以由4个事件来描述:信号产生、信号在进程中注册、信号注销、信号处理函数执行完毕。
有了上面的知识准备,就可以解释上面遗留的两个问题;第一个问题就是为什么SIGINT不可靠信号却好像能够排队,而没有丢弃?这是因为第二次按键是发生在执行信号处理函数期间,而此时之前的SIGINT信号在进程中已经被注销了,那么这时候产生的SIGINT信号就可以被顺利注册。所以其实信号并没有排队。第二个问题为什么按下了6次键却仍然只执行了两次信号处理函数?这个问题就能很好的说明不可靠信号不支持排队,会被丢弃。由于第二次按下ctrl+c键后SIGINT信号已经被注册了,所以接下来产生的5次信号都不能在被注册,即被丢弃,所以只能执行两次信号处理函数。
标签:
原文地址:http://www.cnblogs.com/frank-yxs/p/5929950.html