「Linux 系统编程」信号集操作函数
1 未决信号集和阻塞信号集
1.1 未决信号集 (Pending Signal Set)
当一个信号被发送给进程时(例如,由另一个进程通过 kill() 发送,或由内核产生,如 SIGSEGV),这个信号首先会被加入到该进程的未决信号集中。
未决信号集 是内核为每个进程维护的一个 位图数据结构,用于实时追踪哪些信号已经产生但尚未被进程消费处理。该位图中的 每一位直接映射一个特定的信号编号,位值为 1 表示对应信号处于未决状态。
处理机制:
- 置位操作:当信号产生时,内核通过原子操作将对应位设置为
1。 - 清零时机:当信号被递送给进程处理时,内核将相应位清零。
- 队列语义:对于 标准信号(1-31),多次触发同一信号仅导致单次置位;对于 实时信号(34-64),内核通过 外部队列维护,但未决位图仍记录至少有一个该信号待处理。
特点:
-
一个信号在未决信号集中 最多只会存在一次。即使同一标准信号被发送多次,在未决集里也只会标记为有一个(这是标准信号的“不可靠”性,即可能丢失)。
-
对于实时信号(
SIGRTMIN到SIGRTMAX),多个相同的信号可以在未决队列中排队,不会丢失。
1 | |
1.2 阻塞信号集 / 信号掩码 (Blocked Signal Set / Signal Mask)
阻塞信号集 是进程控制的位图掩码,用于 精确管理信号递送的时机。该位图定义了信号屏蔽策略,其中置位的信号将 被内核暂时抑制递送,实现临界区的原子性保护。
处理机制:
- 屏蔽机制:当位图中某位为
1时,对应信号将被内核阻塞 - 延迟递送:被阻塞的信号产生时,内核 只设置未决位图,而不立即递送信号给进程
- 解除阻塞:当进程 清除阻塞位图中的对应位 时,内核会 检查未决位图并处理积压信号。处理完成后再递送信号给进程。
1 | |
1.3 两集协同工作的位图操作

-
阻塞 决定了一个信号何时被处理(现在还是将来)。
-
未决 记录了一个信号是否已产生但尚未被处理。
2 信号集操作函数
为了设置和修改 阻塞信号集,我们需要一种方法来表示和操作信号集合。Linux 提供了一系列函数来操作一个特殊的类型 sigset_t(信号集类型)。
2.1 数据类型 sigset_t
这是一个不透明数据类型,用于 表示一个信号集合。不能直接操作它的内部位掩码,而 必须使用提供的函数。
2.2 基本操作函数
定义在 <signal.h> 头文件中,成功返回 0,失败返回 -1。
-
int sigemptyset(sigset_t *set)- 作用:初始化一个
sigset_t集合,使其 不包含任何信号(全部清零)。 - 注意:在使用一个新的
sigset_t变量之前,必须 先调用此函数或sigfillset进行初始化,否则它可能包含随机垃圾数据。
- 作用:初始化一个
-
int sigfillset(sigset_t *set)- 作用:初始化一个
sigset_t集合,使其包含所有信号(全部置1)。
- 作用:初始化一个
-
int sigaddset(sigset_t *set, int signum)- 作用:将特定信号
signum添加 到集合set中。
- 作用:将特定信号
-
int sigdelset(sigset_t *set, int signum)- 作用:将特定信号
signum从集合set中 移除。
- 作用:将特定信号
-
int sigismember(const sigset_t *set, int signum)- 作用:检查信号
signum是否在集合set中。 - 返回值:在集合中返回
1,不在返回0,错误返回-1。
- 作用:检查信号
2.3 应用信号掩码 sigprocmask
创建好一个信号集后,我们需要用 sigprocmask 来 设置进程的阻塞信号集(信号掩码)。
sigprocmask 函数:int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
- 作用:检查或修改进程的 阻塞信号集(信号掩码)。
- 参数
how:指定如何修改当前的阻塞信号集。SIG_BLOCK:将set中的信号 加入 当前的阻塞信号集。新的掩码 = 当前掩码 |set。SIG_UNBLOCK:将set中的信号 移除 当前的阻塞信号集。新的掩码 = 当前掩码 & ~set。SIG_SETMASK:直接使用set集合 替换 当前的阻塞信号集。新的掩码 =set。
- 参数
oldset:如果不是NULL,则函数会将 调用前 的阻塞信号集保存到oldset中。时常用于恢复原有掩码。
2.4 获取未决信号集 sigpending
int sigpending(sigset_t *set)
- 作用:获取当前进程的 未决信号集。将当前处于未决状态的信号集合通过
set参数返回。
3 信号操作函数使用示例
下面的示例中,演示了如何阻塞 SIGINT 信号(由 Ctrl+C 产生),然后检查这个信号是否处于未决状态。
1 | |
- 程序启动后,立即阻塞了
SIGINT。 - 在
sleep(5)期间,如果按下Ctrl+C,SIGINT信号会产生。 - 但由于信号被阻塞,它不会被处理,而是被加入到 未决信号集。
sigpending()会检测到它,并打印"SIGINT is pending..."。- 当调用
sigprocmask(SIG_SETMASK, &old_set, NULL)解除阻塞时,内核会立即检查未决信号集。 - 发现有一个未决的
SIGINT,于是立刻将其从未决集中清除,并递送给进程。 - 如果进程没有为
SIGINT设置自定义处理函数,默认行为是终止进程,所以程序会立即退出,最后的printf和sleep(2)可能不会执行。
