Linux多进程17-信号集及相关函数
信号集
- 许多信号相关的系统调用都需要能表示一组不同的信号,多个信号可使用一个称之为信号集的数据结构来表示,其系统数据类型为 sigset_t(64位的整数)。
- 在 PCB(进程控制块) 中有两个非常重要的信号集。一个称之为 “阻塞信号集” (阻塞信号递达),另一个称之为“未决信号集” (没有递达的信号)。这两个信号集都是内核使用位图机制(使用二进制位 )来实现的。但操作系统不允许我们直接对这两个信号集进行位操作。而需自定义另外一个集合,借助信号集操作函数来对 PCB 中的这两个信号集进行修改。
- 信号的 “未决” 是一种状态,指的是从信号的产生到信号被处理前的这一段时间。
- 信号的 “阻塞” 是一个开关动作,指的是阻止信号被处理,但不是阻止信号产生。
- 信号的阻塞就是让系统暂时保留信号留待以后发送。由于另外有办法让系统忽略信号,所以一般情况下信号的阻塞只是暂时的,只是为了防止信号打断敏感的操作。
阻塞信号集和未决信号集
一个模拟的过程:
- 用户通过键盘Ctrl+c, 产生2号信号SIGINT, 信号被创建
- 信号产生, 但没有被处理, 未决状态
- 在内核中将所有没被处理的信号存储在一个集合中, 未决信号集
- SIGINT 信号状态被存储在第二个标志位上, 这个标志位为0说明信号不是未决状态, 标志位为1, 说明信号处于未决状态
- 这个未决状态的信号需要被处理, 处理之前需要和另一个信号集, 进行比较
- 阻塞信号集默认不阻塞任何信号
- 如果想要阻塞某些信号, 需要用户调用系统API
- 在处理的时候, 和阻塞信号集中的标志位进行查询, 看是不是对该信号设置阻塞了
- 如果没有阻塞, 这个信号就被处理
- 如果阻塞了, 这个信号就继续处于未决状态, 知道阻塞解除, 这个信号就被处理
相关函数
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
int sigismember(const sigset_t *set, int signum);
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
int sigpending(sigset_t *set);
以下信号集相关的函数都是对自定义的信号集进行操作
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: 调用失败
代码实例
setitimer.c
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
//创建一个信号集
sigset_t set;
//清空信号集内容
sigemptyset(&set);
//判断SIGINT是否在信号集set里
int ret = sigismember(&set, SIGINT);
if (ret == 0)
{
printf("SIGINT 不阻塞\n");
}
else if (ret == 1)
{
printf("SIGINT 被阻塞\n");
}
else if (ret == -1)
{
perror("sigismember err\n");
exit(0);
}
//添加几个信号到信号集中
sigaddset(&set, SIGINT);
sigaddset(&set, SIGQUIT);
//判断SIGINT是否在信号集set里
ret = sigismember(&set, SIGINT);
if (ret == 0)
{
printf("SIGINT 不阻塞\n");
}
else if (ret == 1)
{
printf("SIGINT 被阻塞\n");
}
else if (ret == -1)
{
perror("sigismember err\n");
exit(0);
}
//判断SIGQUIT是否在信号集set里
ret = sigismember(&set, SIGQUIT);
if (ret == 0)
{
printf("SIGQUIT 不阻塞\n");
}
else if (ret == 1)
{
printf("SIGQUIT 被阻塞\n");
}
else if (ret == -1)
{
perror("sigismember err\n");
exit(0);
}
//从信号集中删除一个信号
sigdelset(&set, SIGQUIT);
//判断SIGQUIT是否在信号集set里
ret = sigismember(&set, SIGQUIT);
if (ret == 0)
{
printf("SIGQUIT 不阻塞\n");
}
else if (ret == 1)
{
printf("SIGQUIT 被阻塞\n");
}
else if (ret == -1)
{
perror("sigismember err\n");
exit(0);
}
return 0;
}
运行程序
$./sigset
SIGINT 不阻塞
SIGINT 被阻塞
SIGQUIT 被阻塞
SIGQUIT 不阻塞
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
- 功能: 将自定义信号集中的数据设置到内核中(设置阻塞, 解除阻塞, 替换)
- 参数:
- how: 如何对内核阻塞信号集进行处理
SIG_BLOCK: 将用户设置的阻塞信号集添加到内核中, 内核中原来的数据不变
假设内核中默认的阻塞信号集是mask, mask | set
SIG_UNBLOCK: 根据用户设置的数据, 对内核中的数据进行解除阻塞
mask &- ~set
SIG_SETMASK: 覆盖内核中原来的值
- set: 已经初始化好的用户自定义的信号集
- oldset: 保存设置之前的内核中的阻塞信号集的状态, 可以是NULL
- 返回值:
成功: 0
失败: -1 错误号:EFAULT / EINVAL
int sigpending(sigset_t *set);
- 作用: 获取内核中的未决信号集
- 参数: set: 传出参数, 保存内核中的未决信号集中的信息
- 返回: 0成功 -1失败
代码实例
编写程序, 把所有常规信号1-31(kill -l)的未决状态打印到屏幕
设置某些信号是阻塞的, 通过键盘产生这些信号
#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("\n");
sleep(1);
if (num == 10)
{
// 解除阻塞
sigprocmask(SIG_UNBLOCK, &set, NULL);
}
}
return 0;
}
运行程序
$./sigpromask
0000000000000000000000000000000
0000000000000000000000000000000
0000000000000000000000000000000
^\0010000000000000000000000000000
0010000000000000000000000000000
^C0110000000000000000000000000000
0110000000000000000000000000000
0110000000000000000000000000000
0110000000000000000000000000000
0110000000000000000000000000000