进程的信号掩码,信号集,sigprocmask函数
目录
核心逻辑
信号集
进程的信号掩码
sigprocmask系统调用
函数原型
参数解释
返回值
示例代码
sigpending系统调用
示例代码
被挂起的信号
进程掩码与sigaction
核心逻辑
正常情况下,进程收到信号后,会执行信号默认的处理动作,例如终止进程。但是有些时候进程在执行一些代码的时候,不希望在这个时候收到信号而终止。于是就需要在执行这部分代码前先阻塞某些信号,执行后解除阻塞。这就是本篇要讲解的事。基本流程如下:
- 在执行这部分代码前,把要阻塞的信号放在信号集中
- 用sigprocmask函数将信号集中的信号阻塞
- 执行这部分不希望接收某些信号的代码
- 解除阻塞
信号集
Linux用数据结构sigset_t表示一组信号集,下列是对信号集的各种操作方法:(后文实操后,大家一目了然)
- int sigemptyset(sigset_t *set);清空一个信号集,即把信号集中所有信号都设置为未包含状态。成功时返回 0,失败时返回 -1。
- int sigfillset(sigset_t *set);将信号集中所有信号都设置为包含状态,即填充信号集。成功时返回 0,失败时返回 -1。
- int sigaddset(sigset_t *set, int signum);将指定的信号 signum 添加到信号集 set 中。成功时返回 0,失败时返回 -1。
- int sigdelset(sigset_t *set, int signum);从信号集 set 中移除指定的信号 signum。成功时返回 0,失败时返回 -1。
- int sigismember(const sigset_t *set, int signum);检查指定的信号 signum 是否在信号集 set 中。如果信号 signum 在信号集 set 中,返回 1;如果不在,返回 0;如果出错(如无效的信号集指针),返回 -1。
进程的信号掩码
进程想要阻塞某些信号,把这些信号都放在一个数据结构sigset_t下的对象,即信号集中。这个信号集就可以被看做进程的信号掩码。
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
:指向一个信号集,该信号集指定了要进行添加、移除或设置的信号。如果how
为SIG_BLOCK
或SIG_UNBLOCK
,set
中信号会按相应规则修改当前信号掩码;如果how
为SIG_SETMASK
,set
直接成为新的信号掩码。若set
为NULL
,则how
的值会被忽略,此时sigprocmask
仅用于获取当前信号掩码。oldset
:指向一个信号集,用于保存旧的信号掩码。如果不需要保存旧的信号掩码,可以将其设置为NULL
。
返回值
成功时返回 0
,失败时返回 -1
,并设置 errno
以指示错误原因。
示例代码
#include <stdio.h>
#include <signal.h>
#include <unistd.h>int main() {sigset_t new_mask, old_mask;sigemptyset(&new_mask);sigaddset(&new_mask, SIGINT);// 阻塞SIGINT信号if (sigprocmask(SIG_BLOCK, &new_mask, &old_mask) == -1) {perror("sigprocmask");return 1;}printf("SIGINT is blocked. Press Ctrl + C to test...\n");sleep(5);// 恢复旧的信号掩码if (sigprocmask(SIG_SETMASK, &old_mask, NULL) == -1) {perror("sigprocmask");return 1;}printf("SIGINT is unblocked.\n");return 0;
}
sigpending系统调用
- 函数原型:
int sigpending(sigset_t *set);
- 作用:获取当前进程中已产生但被阻塞(挂起)的信号集,并将其存储在
set
指向的信号集中。 - 返回值:成功时返回
0
,失败时返回-1
,并设置errno
以指示错误原因
示例代码
#include <stdio.h>
#include <signal.h>
#include <unistd.h>int main() {sigset_t new_mask, pending_mask;sigemptyset(&new_mask);sigaddset(&new_mask, SIGINT);// 阻塞SIGINT信号if (sigprocmask(SIG_BLOCK, &new_mask, NULL) == -1) {perror("sigprocmask");return 1;}// 获取挂起的信号集if (sigpending(&pending_mask) == -1) {perror("sigpending");return 1;}if (sigismember(&pending_mask, SIGINT)) {printf("SIGINT is pending.\n");} else {printf("SIGINT is not pending.\n");}sleep(3);// 获取挂起的信号集if (sigpending(&pending_mask) == -1) {perror("sigpending");return 1;}if (sigismember(&pending_mask, SIGINT)) {printf("SIGINT is pending.\n");} else {printf("SIGINT is not pending.\n");}// 解除对SIGINT信号的阻塞if (sigprocmask(SIG_UNBLOCK, &new_mask, NULL) == -1) {perror("sigprocmask");return 1;}return 0;
}
被挂起的信号
在执行设置阻塞后与解除阻塞前这两者间的代码时,如果收到要被阻塞的信号,该信号会被挂起。在解除阻塞后,该信号会被发送给进程,来执行该信号默认的处理动作。需要注意的是,无论阻塞了几次相同的信号,该信号阻塞在被解除后,该信号都只会被触发一次。
进程掩码与sigaction
不是很清楚sigaction的同学可以看进程信号之sigaction系统调用-CSDN博客
sigprocmask设置的进程掩码是对整个进程的时时刻刻来讲的;sigaction中也要设置一个信号集,用作设置阻塞信号,但是这个阻塞信号是对sigaction所针对的特定信号的处理逻辑执行时来讲的,只有在这个触发这个信号的处理逻辑时才会阻塞这个信号集。
sigaction中要阻塞的信号确切地说是sigaction中设置的信号集与进程掩码的并集。这一点在大家充分理解进程掩码与sigaction系统调用的基础上,不难理解。