- 实际执行信号的处理动作称为信号递达(
Delivery)。 - 信号从产生到递达之间的状态,称为信号未决(
Pending)。 - 进程可以选择阻塞 (
Block)某个信号。 - 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。
- 注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。
😁注意:阻塞信号和我们以前学的进程阻塞是不同的概念,注意区分!
Ⅱ. 信号在内核中的表示
我们之前在学习进程控制块、文件描述符等时候,知道其概念后都会想一件事情,它们在内核中是如何实现的❓❓❓
其实并不难,就是一句话:先描述、再组织!我们上面学过信号在发送的时候其实就是将对应的位图结构中对应的信号编号位置处由 0 置 1,并且其是封装在结构体中的,又好比我们在用 signal 自定义捕捉信号处理方法的时候,OS 是如何知道我们将对应的捕捉方法进行覆盖的呢!下面我们来看看信号集在内核中的表示,下面我们先给出其内核结构大概的草图,并一一来解释:
首先先来讲一下 pending 位图(未决),pending 表示的就是信号未决的状态,如果对应信号编号的比特位为 0,说明还没有收到对应的信号,反之则已经收到了信号!
接着就是 block 位图(阻塞),上面我们讲到我们可以去阻塞某个信号,使得某个信号暂且无法变成递达状态来处理信号。如果对应信号编号的比特位为 0,说明我们不阻塞该信号,反之则阻塞该信号!
那么知道了 pending 和 block 是两个独立的结构,我们也就不难得知,block 是随时都可以设置的,可以先于 pending 置 1 之前设置,也可以晚于 pending 置 1 之后设置,通俗的说就是如果一个信号还没有产生也就是 pending 位图还没被设置对应信号,我们依然可以设置其对应信号的 block 位图也就是阻塞信号!而一旦产生了该信号,那么其就直接被阻塞!直到阻塞被取消才会递达!
最后就是这个函数指针数组!我们上面学过,系统中存在的不同的信号,而不同的信号对应的处理方式可能会不同,为了管理这些信号,内核中就有了这张信号数组,其中每个元素都是一个函数指针,也就是对应着每个信号的地址!
这个信号数组的下标对应着不同信号的编号,而其每个元素就是处理信号的函数。在 task_struct 就会有一个函数数组指针指向这个信号数组,从而达到进程能够快速管理信号的目的!
还记得我们之前使用 signal 函数注册不同信号的处理方法吗,我们可以传递 SIG_DEL 或者 SIG_IGN 表示默认和忽略动作,其实就是判断两个宏即可!比如说对于 SIG_DEL 就是默认动作,系统判断后就直接调用信号表中对应位置信号处理方式,如二号信号就是 SIGINT ,那么就会调用 handler[1] 的处理方法!
但是对于自定义方法就不太一样,首先我们先要定位到是几号信号,比如我们使用 signal(2, mySigHandler),那么就会去找到信号表中的 2 号信号,然后将我们自己写的函数 mySigHandler 覆盖这个信号表的 2 号信号的默认处理方法的地址,最后系统再去调用其处理方法的时候会发现调用的就是我们自己写的处理方法!
以下为参考解释,了解即可:
每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作。信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。在上图的例子中,SIGHUP 信号未阻塞也未产生过,当它递达时执行默认处理动作。
如果 SIGINT 信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。
如果 SIGQUIT 信号未产生过,一旦产生 SIGQUIT 信号将被阻塞,它的处理动作是用户自定义函数 sighandler。 如果在进程解除对某信号的阻塞之前这种信号产生过多次,将如何处理?POSIX.1 允许系统递送该信号一次或多次。Linux 是这样实现的:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。我们这里不讨论实时信号!
Ⅲ. sigset_t 类型
从上面的解释来看,每个非实时信号只有一个 bit 的未决标志,非 0 即 1,不记录该信号产生了多少次,阻塞标志也是这样表示的。因此,未决和阻塞标志可以用相同的数据类型 sigset_t 来存储,sigset_t 称为信号集,这个类型可以表示每个信号的 “有效” 或 “无效” 状态。
💥💥💥其实本质就是一个结构体里面存放着数组!
在阻塞信号集中 “有效” 和 “无效” 的含义是该信号是否被阻塞,而在未决信号集中 “有效” 和 “无效” 的含义是该信号是否处于未决状态。后面将详细介绍信号集的各种操作。
阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask),这里的“屏蔽”应该理解为阻塞而不是忽略。