本文只讨论经典信号!
首先我们应当明确:并非所有的信号都可以忽略或者捕捉:SIGSTOP 和 SIGKILL 是不可忽略或捕捉的,也在本文讨论范围之外!
信号想要抵达进程pcb需要穿过两道大门,分别是未决信号集和屏蔽字信号集,穿过这两道大门,信号才能到达进程PCB,随后,当进程进入内核态时便会发现并处理该信号(时间片的轮转,系统函数的调用,异常等都会导致进程进入内核态,所以不必担心信号不会被进程发现,即使没有系统函数的调用和异常,时间片也会到达,这与线程的pthread_cancel()不同,pthread_cancel()被调用后,只有当目标线程产生系统调用时才会发现并响应该 “事件” )。
首先来说说未决信号集:
未决信号集是由系统置位或复位的,初始情况下,位居信号集所有位都为0,当某个信号通过了未决信号集这扇大门(此时还有一道大门未通过),那么其对应的位就会被系统置1,如果后面还有该种信号到达,看到他们应该走的通道被置1了,就知道此路不通了,于是便会被系统舍弃:这也是为什么说信号不支持排队!(本文只讨论经典信号!)。
接下来信号需要通过的大门就叫屏蔽字信号集,屏蔽字信号集是可以由我们手动替换的,也就是说,我们可以让屏蔽字信号集在初始状态下就将某些位置1(封路啦!)。如果信号到达这里时发现,道路没有被封(置1),就会通过屏蔽字信号集,到达进程PCB,此时,系统又会将屏蔽字信号集中该信号的对应位置1。
欸,不对啊?该信号未决信号集和屏蔽字信号集的对应位都被置1了,那不是之后的信号都进不来了?当然不是,当信号通过屏蔽字信号集之后就会,系统在将该信号屏蔽字信号集对应位置1的同时还会将未决信号集中该信号的对应位置0(解封啦!)。同样的,屏蔽字信号集的对应位会在已经到达进程PCB的信号行为执行完毕之后置0。此时,被堵在屏蔽字信号集大门外的信号就又可以进来啦。
此时不得不聊一下信号的行为,信号有三种行为:默认行为,忽略行为和捕捉行为。其中我们可以将信号行为改为捕捉行为,然后自定义捕捉函数,这样,我们就可以通过信号做各种各样的事情啦!上文所说的信号行为执行完毕之后屏蔽字信号集对应位置0就是说的这个信号行为,也就是说,如果我们将捕捉函数写成一个while(1),那么该信号就永远进不来了,因为信号行为一直无法结束,屏蔽字信号集的对应位一直处于置1状态。
总结一下:在默认情况下,信号的历程一共三个阶段: SIG产生——>未决信号集——>屏蔽字信号集——>信号行为处理。其中通过下一个阶段之前,上一个阶段的对应位就是被屏蔽的状态,信号无法通过,细心的小伙伴应该可以发现:最多有两个相同的信号能同时存在,一个被阻塞在未决信号集和屏蔽字信号集之间,一个已到达PCB。
我们可以通过对屏蔽字信号集设初值的方式来屏蔽目标信号,这个没什么好说的,各位小伙伴可以自行测试一下。
struct sigaction结构体里有一个sa_mask,称为临时屏蔽字,有时当特定信号在执行捕捉函数的时候我们希望能够屏蔽其他的某种信号,这时我们就可以通过这个临时屏蔽字实现,当信号行为处理完毕后,临时屏蔽字就会被解除。临时屏蔽字和我们平时所用的sigaction()其实都是对信号字屏蔽集进行操作,只不过临时屏蔽字是在目标信号到达PCB后系统自动替换的。
比如:
上图中我们让SIGINT信号行为处理时屏蔽SIGQUIT信号,所以当SIGINT信号捕捉函数执行时,按下ctrl + \ (SIGQUIT)没有反应。
但假如我们在捕捉函数中将信号字屏蔽集还原,此时再用SIGQUIT就会发现其该信号又没有被屏蔽了!
小伙伴们可自行测试。
嗯,结束啦,
学而时习之。
温故而知新。