信号的产生、阻塞以及捕捉
用kill -l命令可以查看系统定义的信号列表:
一、产生信号
1、如何产生信号?
(1)通过终端按键产生信号
SIGINT的默认处理动作是终止进程,SIGQUIT的默认处理动作是终止进程并且 Core Dump,那么到底什么是Core Dump?
当一个进程要异常终止时,可以选择把进程的用户空间内存数据全部保存到磁盘上,文件名通常是core,这叫做Core Dump。进程异常终止通常是因为有 Bug,比如非法内存访问导致段错误,事后可以用调试器检查core文件以查清错误原因,这叫做 Post-mortem Debug。一个进程允许产生多大的core文件取决于进程的Resource Limit(这 个信息保存在PCB中)。默认是不允许产生core文件的,因为core文件中可能包含用户密码等敏感信息,不安全。在开发调试阶段可以用ulimit命令改变这个限制,允许产生core文 件。
首先用ulimit命令改变Shell进程的Resource Limit,允许core文件最大为1024K:
测试代码:
(2)调用系统函数向进程发信号
如:
1、kill函数:给指定的进程发送指定的信号
2、raise函数:可给当前进程发送指定的信号(自己给自己发送信号)
3、abort函数:使当前进程接受到SIGABRT信号而异常终止
4、signal函数:设置某一信号的对应动作
测试代码:
(3)由软件条件产生信号
如:alarm函数:在seconds秒后给当前进程发送SIGALRM信号,默认终止当前进程。
(4)kill命令行命令,如下:
2、如何处理信号
(1)忽略此信号
(2)执行该信号的默认处理动作
(3)提供一个信号处理函数,要求内核在处理该信号是切换时切换到用户态执行这个处理函数,也称捕捉一个信号。
二、阻塞信号
实际执行信号的处理动作称为信号递达(Delivery),信号从产生到递达之间的状态,称为信号未决(Pending)。进程可以选择阻塞 (Block )某个信号。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞, 才执行递达的动作。注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。
信号在内核中的表示示意图:
每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作。
1、SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作。
2、SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没 有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。
3、SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler。
每个信号只有一个bit的未决标志和阻塞标志,非0即1,不记录该信号产生的次数。因此,未决和阻塞标志可以用相同的数据类型sigset_t储存,sigset_t称为信号集。这个类型表示信号的有效和无效状态。在阻塞信号集(信号屏蔽字)中即block表,1表示信号阻塞,0表示不阻塞。在未决信号集即pending表,1表示信号产生未决状态,0表示没有产生信号。阻塞信号集也称为当前进程的信号屏蔽字(Singal Mask)。这里的屏蔽应该理解为阻塞而不是忽略。
注:不可使用位操作操作信号集,有专有的函数操作
信号集操作函数:
sigemptyset函数:初始化set所指向的信号集,将其中所有的信号对应的bit位清零,表示该信号集不包含任何有效信号。
sigfillset函数:初始化set所指向的信号集,使其中所有的信号对应的bit置位,表示该信号集的有效信号包括系统支持的所有信号。
sigaddset函数:在该信号集中添加某一信号。
sigdelset函数:在该信号集中删除某一个信号。
sigismember函数:判断某一个信号是否在该信号集中。
注意:在使用sigset_t类型的变量之前,一定要调用sigemptyset和sigfillset做初始化处理,使信号集处于确定的状态。初始化之后就可以在该信号集中增加,删除某种有效信号等操作。
sigprocmask函数:读取或更改进程中的信号屏蔽字(即阻塞信号集)
如果set是非空指针,则读取进程的当前信号屏蔽字通过oldset参数传出。
sigpending函数:读取当前信号集的未决信号集
测试代码:
运行效果:
三、捕捉信号
一次信号捕捉的过程需要进行4次权限的切换。
当一个进程接受到信号时,并不是立即进行处理,而是在合适的时候处理,所谓合适是指从内核态返回用户态时切换处理信号。当内核处理完异常或中断时,会先检查当前进程中是否有可以被递达的信号,若有,且信号的处理动作是自定义的信号处理函数,则从内核调到用户态执行代码,之后进入内核态,从内核态返回用户态即上次被中断或异常的地方,若信号的处理方式是默认,则终止进程,若忽略,则从penging表中删除该信号,即将1变为0,直接跳到用户态。
sigaction函数:读取和修改与指定信号相关联的处理动作。
参数:
signo:信号的编号
act:非空时,根据act修改该信号的处理动作
oact:非空时,传出该信号原来的处理动作
act和oact结构体如下:
将sa_handler赋值为常数SIG_IGN传给sigaction表示忽略信号,赋值为常数SIG_DFL 表示执行系统默认动作,赋值为一个函数指针表示用自定义函数捕捉信号,或者说向内核注册 了一个信号处理函数,该函数返回值为void,可以带一个int参数,通过参数可以得知当前信 号的编号,这样就可以用同一个函数处理多种信号。显然,这也是个回调函数,不是被main 函数调用,而是被系统所调用。
sa_mask:需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字。
sa_flags:包含一些选项
pause函数:使调用的进程挂起直到有信号递达。
1、信号的处理动作是终止,则进程终止,不执行pause(即来不及执行该函数)。
2、信号的处理动作是忽略,则进程继续处于挂起,pause不返回。
3、信号的处理动作是捕捉,则调用信号处理函数后返回-1.
测试代码:
运行效果:
测试普通信号中哪些可以被捕捉到,哪些不能被捕捉到。
测试代码:
结论:除了9和19号信号,其他的普通信号都可以被捕捉到。