您现在的位置是:首页 >技术教程 >【Linux】信号集及相关函数(sigemptyset、sigfillset、sigprocmask)网站首页技术教程
【Linux】信号集及相关函数(sigemptyset、sigfillset、sigprocmask)
橙色
1、信号集
-
多个信号组成的一个集合称为信号集,其系统数据类型为 sigset_t 。
-
在 PCB 中有两个非常重要的信号集,一个称为“阻塞信号集”,另一个是“未决信号集”。信号产生但是没有被处理 (未决),在内核中将所有的没有被处理的信号存储在一个集合中 (未决信号集)。这个未决状态的信号,需要被处理,处理之前需要和另一个信号集(阻塞信号集),进行比较,在处理的时候和阻塞信号集中的标志位进行查询,看是不是对该信号设置阻塞了。
-
信号的“未决”是一种状态,指的是从信号的产生到信号被处理前的这一段时间。
-
信号的“阻塞”是一个开关动作,指的是阻止信号被处理,但不是阻止信号产生。
-
信号的阻塞就是让系统暂时保留信号留待以后发送。由于另外有办法让系统忽略信号.所以一般情况下信号的阻塞只是暂时的,只是为了防止信号打断敏感的操作。
2、自定义信号集相关函数
以下信号集相关的函数都是对自定义的信号集进行操作
/*
int sigemptyset(sigset_t *set);
- 功能:清空信号集中的数据,将信号集中的所有的标志位置为0
- 参数:set,传出参数,需要操作的信号集
- 返回值:成功返回0, 失败返回-1
int sigfillset(sigset_t *set);
- 功能:将信号集中的所有的标志位置为1
- 参数:set,传出参数,需要操作的信号集
- 返回值:成功返回0, 失败返回-1
int sigaddset(sigset_t *set, int signum);
- 功能:设置信号集中的某一个信号对应的标志位为1,表示阻塞这个信号
- 参数:
- set:传出参数,需要操作的信号集
- signum:需要设置阻塞的那个信号
- 返回值:成功返回0, 失败返回-1
int sigdelset(sigset_t *set, int signum);
- 功能:设置信号集中的某一个信号对应的标志位为0,表示不阻塞这个信号
- 参数:
- set:传出参数,需要操作的信号集
- signum:需要设置不阻塞的那个信号
- 返回值:成功返回0, 失败返回-1
int sigismember(const sigset_t *set, int signum);
- 功能:判断某个信号是否阻塞
- 参数:
- set:需要操作的信号集
- signum:需要判断的那个信号
- 返回值:
1 : signum被阻塞
0 : signum不阻塞
-1 : 失败
*/
代码举例
#include <signal.h>
#include <stdio.h>
int main() {
// 创建一个信号集,一开始可能有某些标志位为1,所以下一步需要清空
sigset_t set;
// 清空信号集的内容
sigemptyset(&set);
// 判断 SIGINT 是否在信号集 set 里
int ret = sigismember(&set, SIGINT);
if(ret == 0) {
printf("SIGINT 不阻塞
");
} else if(ret == 1) {
printf("SIGINT 阻塞
");
}
// 添加几个信号到信号集中
sigaddset(&set, SIGINT);
sigaddset(&set, SIGQUIT);
// 判断SIGINT是否在信号集中
ret = sigismember(&set, SIGINT);
if(ret == 0) {
printf("SIGINT 不阻塞
");
} else if(ret == 1) {
printf("SIGINT 阻塞
");
}
// 判断SIGQUIT是否在信号集中
ret = sigismember(&set, SIGQUIT);
if(ret == 0) {
printf("SIGQUIT 不阻塞
");
} else if(ret == 1) {
printf("SIGQUIT 阻塞
");
}
// 从信号集中删除一个信号
sigdelset(&set, SIGQUIT);
// 判断SIGQUIT是否在信号集中
ret = sigismember(&set, SIGQUIT);
if(ret == 0) {
printf("SIGQUIT 不阻塞
");
} else if(ret == 1) {
printf("SIGQUIT 阻塞
");
}
return 0;
}
3、sigprocmask函数
函数解析
/*
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
- 功能:将自定义信号集中的数据设置到内核中(设置阻塞,解除阻塞,替换)
- 参数:
- how : 如何对内核阻塞信号集进行处理
SIG_BLOCK: 将用户设置的阻塞信号集添加到内核中,内核中原来的数据不变
假设内核中默认的阻塞信号集是mask, mask | set
SIG_UNBLOCK: 根据用户设置的数据,对内核中的数据进行解除阻塞
mask &= ~set(也就是先对set取反,再与mask按位与),假设内核中默认的阻塞信号集是mask
SIG_SETMASK:覆盖内核中原来的值
- set :已经初始化好的用户自定义的信号集
- oldset : 保存设置之前的内核中的阻塞信号集的状态,可以是 NULL
- 返回值:
成功:0
失败:-1
设置错误号:EFAULT、EINVAL
int sigpending(sigset_t *set);
- 功能:获取内核中的未决信号集
- 参数:set,传出参数,保存的是内核中的未决信号集中的信息。
*/
代码举例
该代码首先自定义了信号集 set,并把 SIGINT 和 SIGQUIT 信号放入其中。然后又通过 sigprocmask 函数修改了内核中的阻塞信号集。接着设置了死循环,在循环内创建了一个信号集pendingset,并将当前的未决信号集的数据放入pendingset,接着遍历该信号集的前31位。
当你运行该程序时,首先输出的都会是0,接着在循环10次之前,按ctrl+c,打印出的第二位就会从0变成1(其实也就是你按ctrl+c的时候,就代表向系统发送 SIGINT 信号,但该信号通过上面的操作在内核中的阻塞信号集中已经被设置为1了,所以当系统接收到该信号时,也就会阻塞,因此该信号状态为未决,打印出的未决信号集的第二位(代表SIGINT)也就变为了1)。
10次循环之后,解除了阻塞。之前按ctrl+c也没办法停止死循环,但按完ctrl+c且10次循环之后,死循环就会自动停止,因为程序设置10次之后解除我们之前设置的信号阻塞,此时系统不再阻塞之前所发送的SIGINT 信号,于是SIGINT 信号发挥自己的作用,停止了该进程。
// 编写一个程序,把所有的常规信号(1-31)的未决状态打印到屏幕
// 设置某些信号是阻塞的,通过键盘产生这些信号
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
// 设置2、3号信号阻塞
sigset_t set;
sigemptyset(&set);
// 将2号和3号信号添加到信号集中
sigaddset(&set, SIGINT);
sigaddset(&set, SIGQUIT);
// 修改内核中的阻塞信号集
sigprocmask(SIG_BLOCK, &set, NULL);
int num = 0;
while(1) {
num++;
// 获取当前的未决信号集的数据
sigset_t pendingset;
sigemptyset(&pendingset);
sigpending(&pendingset);
// 遍历前32位
for(int i = 1; i <= 31; i++) {
if(sigismember(&pendingset, i) == 1) {
printf("1");
}else if(sigismember(&pendingset, i) == 0) {
printf("0");
}else {
perror("sigismember");
exit(0);
}
}
printf("
");
sleep(1);
if(num == 10) {
// 解除阻塞
sigprocmask(SIG_UNBLOCK, &set, NULL);
}
}
return 0;
}