!声明:按照Linux的习惯,我的这篇文档也遵循GPL协议:你可以随意应用并修改本文档,必须发布你的修改,使其他人可以获得一份Copy,尤其是给我一份Copy!我的mail:bob_zhang2004@163.com|zhanglinbao@gmail.com均可。欢迎论坛转载!目前有些内容已经在中进行过讨论,可以前往:=&Board=linuxK&Number=607800&page=0&view=&sb=&o=&fpart=&vc=1和=&Board=linuxK&Number=607228&page=1&view=collapsed&sb=5&o=7&fpart=欢迎大家继续讨论,以便文档更加完善!多谢!周末愉快!--bob读这份文档之前,建议先浏览一下《UnixAdvancedProgramming》里面的signal一章和下面这份出自IBM论坛的文章:进程间通信信号(上),和进程间通信信号(下)该作者写了一个系列的进程间通信的文章,我只是希望对该篇作个补充!因为它们都没有从源代码的角度分析,所以我尝试了一下把上层应用与kernel实现代码分析结合起来,这样使用者才可能真正的理解signal的用法和原理!目前介绍signal理论和用法书不少,缺点是只介绍其用法,非常深奥拗口,不容易理解;而介绍kernel源代码的书,侧重于代码分析,不讲实际应用!我就想到如果把两者结合起来,对上层使用signal函数的用户必然能知起所以然了,而且只要顺着我的代码注释大概粗读一下源码就可以理解signal的特性和用法以及你碰到的种种疑惑和不解了。如果你对signal的特性和用法有什么疑惑的话,如果对kernel也感兴趣的话,就可以继续读源码,把这篇文章加以补充和完善!前提是遵守上面的声明!因为工作的需要,详细的读了一下linuxkernel2.4.24版本的signal部分的源代码,收获不小。以前读UnixAdvancedProgramming的时候,对signal的掌握只是停留在表面,只是会用而已,但是并不知道它是怎么实现,signal的本质到底是什么。读了源码之后,才真正知道了signal的奥妙所在,对其用法理解的就更深了,主要的是以后用signa的时候,犯错的机会就少了。有的时候看着一个系统调用成堆的手册页,还真不如看看它的实现来得更快,当然两下对照着看就快了。在此通过阅读源码,弄清楚了5个问题,每个问题我都给出了结论,当然这些结论肯定是正确的,至少《UnixAdvancedProgramming》是这样认为的,我只是从kernel的角度是验证它的正确性(简单的写了几个测试程序,以验证kernel的做法),而且也归纳了一些结论,比如如何避免Zobie进程等。相信对大家会有价值,也可以mail讨论!或者上相应的论坛!当然有个别的也是我自己的结论,如果您认为有值得商榷的地方,可以Email给我:bob_zhang2004@163.com首先总结一下:在PClinux(RHT9.0+kernel-2.4.24)键盘产生的信号:Ctrl+cSIGINT(2)terminate,以前我总想当然以为是SIGTERM(15)!Ctrl+\SIGQUIT(3)terminateCtrl+zSIGTSTP(20)挂起进程对于一般应用:挂起一个进程:kill(pid,SIGSTOP)或kill(pid,SIGTSTP),或SIGTTIN,SIGTTOU信号恢复一个进程kill(pid,SIGCONT);杀死所有的符合某个名字的进程:比如killallcurl,发送的是SIGTERM信号强制杀死某个进程kill–9curl,发送的是SIGKILL信号,在kernel中,SIGKILL和SIGSTOP是不能被忽略的....剩下的大家都清楚了,这里就不罗嗦了。子进程结束时候发给父进程的信号:SIGCHLD,这个比较特殊,且看下面3的论述Agenda:1不可靠的信号2Zombie进程(僵尸进程)与signal3特殊的SIGCHLD信号4信号与进程的关系,进程的要求5pause()与signal6关于阻塞信号7关于不可重入函数8关于信号的技巧1不可靠的信号(linux继承Unix的结果,考虑兼容性),和可靠的信号(主要就是信号可以排队处理,信号不丢失,linux自己的,但大家好像用的不多)什么是不可靠的信号:简单的说,就是当你向一个进程发送singal(1~31,注意这里讨论是1~31)的时候,当进程还没有处理该信号(这时候叫pending,未决信号)或者是正在调用信号处理函数的时候,进程又收到了一个同样的信号,kernel会把第二个信号丢弃,或者叫和一个信号合并,这样的信号就是不可靠的信号,具体正方面的比较权威的解释请参考这篇文章对于信号理论介绍的非常详细清楚明白,个人认为比《UnixadvancedProgramming》要更好!系统实现是这样的:==kernel/signal.cintsend_sig_info(intsig,structsiginfo*info,structtask_struct*t){............................................./*如果当前进程的未决信号集中已经包括了这个信号,就不重新注册后来现在的同样的信号了,据个例子:给进程发了SIGTERM信号,但是kernel还没有来得及处理(进程只有在kernel空间即将返回道用户空间的时候,kernel才会检测pending信号,然后才会调用do_signal()函数去处理)这个时候又发了一个SIGTERM,那么第二个SIGTERM肯定要被cut掉了。*/if(sigSIGRTMIN&&sigismember(&t-pending.signal,sig))//SIGRTMIN是分水岭,小于它的都是不可靠的信号,否则就是实时信号gotoout;//跳出了正常执行的范围....................................................}!正确的:1~31都是不可靠的信号!SIGRTMIN~SIGRTMAX都是可靠的信号!以前大家有个误区:!误区1以为不可靠的信号,是指给进程发了一个信号(之前没有发过),那么这个信号可能丢失,也就是进程收不到这样的理解是错误的,根据上面的定义,应该是”一个信号发了多遍,后来的信号丢失了,而不是第一个丢了“。具体的原因可以参照上面的代码分析,就一目了然,还可以看《unixadvancedprogramming》,不过我觉得它讲的都是老的Unix,对Linux只能是参考而已!!误区2signal()发送的是不可靠的信号,而sigaction()发送的是可靠的信号只要是1-31的信号,它就是不可靠的信号。无论在注册信号处理函数的时候用的是sigaction(),还是signal(),只要你发送的信号是1-31,那么就是不可靠的信号。中国有句俗语叫”烂泥扶不上墙“,我看放在这里挺合适!signal()和sigaction()的差别到底在哪里呢?通过对比一看便知:对于signal(),它的kernel实现函数,也叫系统调用服务历程sys_signal()==kernel/signal.casmlinkageunsignedlongsys_signal(intsig,__sighandler_thandler){structk_sigactionnew_sa,old_sa;intret;new_sa.sa.sa_handler=handler;new_sa.sa.sa_flags=SA_ONESHOT|SA_NOMASK;//SA_ONESHOT:当执行一次信号处理程序后,马上恢复为SIG_DFL,//SA_NOMASK:表示在信号处理函数执行期间,不屏蔽的当前正在处理的那个信号ret=do_sigaction(sig,&new_sa,&old_sa);//sys_sigaction也调用这个函数returnret?ret:(unsignedlong)old_sa.sa.sa_handler;}而sigaction()函数的kernel实现是:sys_sigaction()==arch/i386/kernel/signal.casmlinkageintsys_sigaction(intsig,conststructold_sigaction*act,structold_sigaction*oact){structk_sigactionnew_ka,old_ka;intret;if(act){old_sigset_tmask;if(verify_area(VERIFY_READ,act,sizeof(*act))||__get_user(new_ka.sa.sa_handler,&act-sa_handler)||__get_user(new_ka.sa.sa_restorer,&act-sa_restorer))return-EFAULT;__get_user(new_ka.sa.sa_flags,&act-sa_flags);__get_user(mask,&act-sa_mask);siginitset(&new_ka.sa.sa_mask,mask);}ret=do_sigaction(sig,act?&new_ka:NULL,oact?&old_ka:NULL);//都调的这个函数if(!ret&&oact){if(verify_area(VERIFY_WRITE,oact,sizeof(*oact))||__put_user(old_ka.sa.sa_handler,&oact-sa_handler)||__put_user(old_ka.sa.sa_restorer,&oact-sa_restorer))return-EFAULT;__put_user(old_ka.sa.sa_flags,&oact-sa_flags);__put_user(old_ka.sa.sa_mask.sig[0],&oact-sa_mask);}returnret;}signal()和sigaction()都是用do_signaction()来包装的,都是用structsigaction()这个结构体的,差别在下面标出来了structsigaction{__sighandler_tsa_handler;//2//typedefvoid(*__sighandler_t)(int);signal()和sigaction()函数都要求要户提供信号处理函数unsignedlongsa_flags;//signal()函数默认就用SA_ONESHOT|SA_NOMASK;//sigaction()要由用户自己指定!void(*sa_restorer)(void);//没用了sigset_tsa_mask;//执行信号处理函数的时候要阻塞的信号,signal()使用默认的,就屏蔽正处理的信号,其他的不屏蔽,sigaction()要求用户自己指定!};?讨论时间:读到这里我有个疑问:sys_signal(