目录
一、 信号三大阶段
二、信号储存相关概念
- 实际执行信号的过程被称为信号递达(Delivery)。
- 信号从产生到递达之间的状态被称为信号未决(Pending)。
- 进程可以选择阻塞(Block)某个信号。
- 被阻塞的信号产生时将保存在未决状态,直到进程解除对此信号的阻塞,才会执行递达动作。
- 注意:阻塞和忽略是不同的,信号被阻塞就不会递达,而忽略是递达之后可以选择的一种执行方式。
三、 理解概念
理解一下过程:
一般来说,操作系统向进程写入一个信号,信号先是被阻塞(假设设置了对这个信号的阻塞)什么都不会做;接着进程解除了对该信号的阻塞,信号进入未决状态(就是进程执行相关任务前的这段时间);进程到了合适的时间,就会执行信号对应的相关操作(也就是信号递达)。
简单理解就是:写入信号——看是否被阻塞——解除阻塞进入未决状态——进程执行相关操作。
详细理解一下每个步骤:
1、假设你在读书的时候你很喜欢一个老师,老师在课上布置了一个课后作业,你把这个作业先记录到了小本子上面,然后回家后就立马写作业。
这个例子话黑线部分就分别对应了信号未决状态和信号递达状态。
2、假设你一般喜欢另一个老师,但是他不怎么检查作业,一天上课它布置了一个课后作业,你也拿小本子记了下来,除了记在作业那一页,你还记在里另一页上表示这个作业不急着写,因为反正老师也不检查,你就想只完成例子一的作业不就行了。但是有一天这个老师说明天要检查作业,你就把它从不急着写那里划掉了。然后晚上将这个作业完成。
这个例子中的黑体分别对应了信号阻塞、信号未决和信号递达状态。
四、信号储存原理
信号是被存放在进程里的,更具体的说,是存放在task_struct里的——用于管理进程的结构体。除了实时信号我们用的到的信号就31个,那么我们用一个int类型的数据就可以把一个信号储存起来——也就是位图结构,位图的位数表示几号信号,某一位上的0、1表示是否有接收到该信号。————这就对应了task_struct里的pending,这个几十用于存放信号的容器。
00000000000000000000000000000011
这个就表示该进程接收到了12号信号。
同理的我们也可以用相同的结构,存放一些数据来表示一个信号是否被阻塞————与之对应的是block。
00000000000000000000000000000001
这里就表示1号进程被阻塞了。
这两个都是用sigset_t的数据类型。阻塞信号集也被称之为当前进程的信号屏蔽字,这里的屏蔽应该理解成阻塞而不是忽略。
还剩下一个非常关键的东西:handler数组。我们有31个信号,这个数组就有31个元素,分别对应接受到每个信号时,我们分别需要做什么。这也就是为什么一个进程会认识这些信号,因为收到信号要做什么都是早就被设置好的。
五、信号集操作函数
sigset_t 是一个操作系统内核中的数据结构,用于表示信号集。在使用信号相关的系统调用时,会涉及到对 sigset_t 进行操作,例如添加、删除、屏蔽、解除屏蔽等。这些操作需要使用一些特定的函数来实现,因为这些函数通常会与操作系统内核进行交互。此外,操作 sigset_t 需要涉及到底层的机制,例如信号处理器、信号屏蔽、信号队列等,这些机制通常需要由操作系统内核来管理。因此,只能使用函数来操作 sigset_t。
上面这段话总而言之就是:要对sigset_t进行操作只能通过一下几个函数:
#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset (sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo);
- 函数sigemptysetchushihuaset所指向的信号集,使其所有bit位置0,表示该信号不包含任何有效信号。
- 函数sigfillset初始化set所指的信号集,使其中所有信号对应bit位置表示该信号集的有效信号集包括系统支持的所有信号。
- 注意,在使用sigset_t类型变量之前,一定要调用sigemptyset或者sigfill做初始化,使信号处于确定的状态,初始化sigset_t变量后就可以在调用sigaddset和sigdelset在该信号添加或者删除某种有效信号。
- 函数sigaddset,参数signo表示要在set信号集中添加哪个信号。
- 这四个函数都是成功返回0,失败返回-1.sigismenber是一个布尔函数,用于判断一个信号集的有效信号中是否包含某种信号,若包含则返回1,不包含则返回0,出错则返回-1。
上面这些函数都是对sigset_t信号集操作的,但是我要怎么阻塞或则读取信号呢?如果不能实现这个功能的话,那岂不是上面这些函数也没什么用呀。
sigprocmask函数,调用这个函数可以读取或则更改进程的信号屏蔽字(阻塞信号集)。
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
//返回值:成功返回0,失败返回-1
- 参数set就是我们上面用那些函数产生的一个sigset_t信号集。
- 参数oset是一个输出型参数,就是oldset的缩写,因为我们可能会对进程内原有的信号集进行修改,所以我们可以保留进程内原有的set,将它写入oset。
- 第一个参数how就是你希望执行的操作模式有一下三种:
函数sigpending()
#include <signal.h>
sigpending(&sigset_t set);
//读取当前进程的未决信号集,通过set参数传出。调用成功则返回0,出错则返回-1。
实操例子:
运行结果: