目录
sigprocmask
用来屏蔽信号、解除屏蔽也使用该函数。其本质,在PCB中读取或修改进程的信号屏蔽字。
注意:屏蔽信号只是将信号处理延后执行(延至解除屏蔽),而忽略表示将信号丢处理。
man 2 sigprocmask
参数how
SIG_BLOCK:设置阻塞
SIG_UNBLOCK:取消阻塞
SIG_SETMASK:用自定义的set替换mask
参数set
自定义set
参数oldset
保存旧的信号屏蔽集。
返回值
成功:0
失败:-1,设置errno
sigpending
查看未决信号集。
man 2 sigpending
参数set
传出的未决信号。
测试代码1
屏蔽CTRL+C、CTRL+\、7)、9)信号。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
int main(int argc, char *argv[])
{
sigset_t XinHaoJi; //信号集
sigset_t Old_XinHaoJi; //旧的信号集
sigset_t WeiJue_XinHao; //未决信号
int flag, i;
sigemptyset(&XinHaoJi); //清空信号集
sigaddset(&XinHaoJi, SIGINT); //屏蔽Ctrl+C信号
sigaddset(&XinHaoJi, SIGQUIT); //屏蔽Ctrl+\信号
sigaddset(&XinHaoJi, SIGBUS); //屏蔽7)信号
sigaddset(&XinHaoJi, SIGKILL); //屏蔽9)信号
flag = sigprocmask(SIG_BLOCK, &XinHaoJi, &Old_XinHaoJi); //设置阻塞屏蔽信号
if (flag == -1)
{
perror("设置阻塞屏蔽信号错误");
exit(1);
}
while (1)
{
flag = sigpending(&WeiJue_XinHao); //获取未决信号
if (flag == -1)
{
perror("获取未决信号错误");
exit(1);
}
printf("未决信号集:");
for (i = 1; i < 32; i++)
{
printf("%d", sigismember(&WeiJue_XinHao, i)); //判断一个信号是否在集合中
}
printf("\n");
sleep(2);
}
return 0;
}
测试结果
只有9)信号不能被屏蔽,能够杀死进程。
signel
注册一个信号捕捉函数。
man 2 signel
测试代码2
捕捉CTRL+C信号,捕捉后输出内容。
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
void ChuLi_HanShu()
{
static int i = 1;
printf("成功捕捉到%d次。\n", i++);
}
int main(int argc, char *argv[])
{
signal(SIGINT, ChuLi_HanShu); //注册信号捕捉函数,捕捉Ctrl+C信号
printf("开始捕捉Ctrl+C信号!\n");
while (1)
;
return 0;
}
测试结果
sigaction
注册一个信号捕捉函数。
man 2 sigaction
参数act
传入参数,新的处理方式。
参数oldact
传出参数,旧的处理方式。
返回值
成功:0
失败:-1
测试代码3
用sigaction捕捉Ctrl+C、Ctrl+\信号,捕捉后输出内容。
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
void ChuLi_HanShu(int XinHao)
{
static int i = 1;
printf("成功捕捉到%d次,捕捉到的信号是%d。\n", i++, XinHao);
}
int main(int argc, char *argv[])
{
int flag;
struct sigaction New_ChuLi_FangShi; //新的处理方式
struct sigaction Old_ChuLi_FangShi; //旧的处理方式
New_ChuLi_FangShi.sa_handler = ChuLi_HanShu; //设置处理函数
sigemptyset(&New_ChuLi_FangShi.sa_mask); //清空信号集
New_ChuLi_FangShi.sa_flags = 0; //默认值处理
flag = sigaction(SIGINT, &New_ChuLi_FangShi, &Old_ChuLi_FangShi); //注册信号捕捉函数
if (flag == -1)
{
perror("注册捕捉信号函数失败");
}
flag = sigaction(SIGQUIT, &New_ChuLi_FangShi, &Old_ChuLi_FangShi); //注册信号捕捉函数
if (flag == -1)
{
perror("注册捕捉信号函数失败");
}
printf("开始捕捉Ctrl+C、Ctrl+\\信号!\n");
while (1)
;
return 0;
}
测试结果
信号捕捉特性
捕捉函数执行期间,信号屏蔽字由mask变成sa_mask,捕捉函数执行结束。再由sa_mask恢复回mask。
捕捉函数执行期间,本信号自动被屏蔽,sa_flags=0。
捕捉函数执行期间,被屏蔽信号多次发送,解除屏蔽后只处理一次。
测试代码4
按照Ctrl+\、Ctrl+C、Ctrl+\、Ctrl+C的顺序发送信号,查看执行结果。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void ChuLi_HanShu(int XinHao)
{
static int i = 1;
if (XinHao == SIGINT)
{
printf("\n成功捕捉到%d次,捕捉到的信号是%d,Ctrl+C开始睡10s。\n", i++, XinHao);
sleep(10);
printf("\nCtrl+C睡醒了。\n");
}
else if (XinHao == SIGQUIT)
{
printf("\n成功捕捉到%d次,捕捉到的信号是%d,Ctrl+\\开始睡10s。\n", i++, XinHao);
sleep(10);
printf("\nCtrl+\\睡醒了。\n");
}
}
int main(int argc, char *argv[])
{
int flag;
struct sigaction New_ChuLi_FangShi; //新的处理方式
struct sigaction Old_ChuLi_FangShi; //旧的处理方式
New_ChuLi_FangShi.sa_handler = ChuLi_HanShu; //设置处理函数
sigemptyset(&New_ChuLi_FangShi.sa_mask); //清空信号集
New_ChuLi_FangShi.sa_flags = 0; //默认值处理
flag = sigaction(SIGINT, &New_ChuLi_FangShi, &Old_ChuLi_FangShi); //注册信号捕捉函数
if (flag == -1)
{
perror("注册捕捉信号函数失败");
}
flag = sigaction(SIGQUIT, &New_ChuLi_FangShi, &Old_ChuLi_FangShi); //注册信号捕捉函数
if (flag == -1)
{
perror("注册捕捉信号函数失败");
}
printf("开始捕捉Ctrl+C、Ctrl+\\信号!\n");
while (1)
;
return 0;
}
测试结果
推测:
第1次捕捉到3)信号,未决信号3)置1,处理函数开始执行,未决信号3)清零。
第2次捕捉到2)信号,未决信号2)置1,中断第1次的处理函数,第2次处理函数开始执行,未决信号2)清零。
3)信号、2)信号按顺序到达,未决信号3)置1,未决信号2)置1。
第2次处理函数执行结束,检测未决信号2),发现还是1,继续执行信号2)第3次的处理函数,未决信号2)清零。
第3次的处理函数执行完成后,继续执行第1次的处理函数。
第1次处理函数执行完成后,检测未决信号3),发现还是1,继续执行信号3)第4次的处理函数。
总结:每执行完对应的信号处理函数,都再次检测未决信号,如果仍有未决信号,则继续执行处理函数;如果没有未决信号,则继续执行中断后的处理函数。
测试代码5
捕捉到1个信号后,在执行中,再捕捉到下一个信号,下一个信号延迟执行。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void ChuLi_HanShu(int XinHao)
{
static int i = 1;
if (XinHao == SIGINT)
{
printf("\n成功捕捉到%d次,捕捉到的信号是%d,Ctrl+C开始睡10s。\n", i++, XinHao);
sleep(10);
printf("\nCtrl+C睡醒了。\n");
}
}
int main(int argc, char *argv[])
{
int flag;
struct sigaction New_ChuLi_FangShi; //新的处理方式
struct sigaction Old_ChuLi_FangShi; //旧的处理方式
New_ChuLi_FangShi.sa_handler = ChuLi_HanShu; //设置处理函数
sigemptyset(&New_ChuLi_FangShi.sa_mask); //清空信号集
sigaddset(&New_ChuLi_FangShi.sa_mask, SIGQUIT); //延迟执行的信号
New_ChuLi_FangShi.sa_flags = 0; //默认值处理
flag = sigaction(SIGINT, &New_ChuLi_FangShi, &Old_ChuLi_FangShi); //注册信号捕捉函数
if (flag == -1)
{
perror("注册捕捉信号函数失败");
}
printf("开始捕捉Ctrl+C、Ctrl+\\信号!\n");
while (1)
;
return 0;
}
测试结果
内核实现信号捕捉过程
测试代码6
借助信号捕捉回收子进程,全部回收后,父进程再继续执行。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <wait.h>
void ChuLi_HanShu(int XinHao)
{
int flag;
pid_t JinCheng_ID; //进程ID
while ((JinCheng_ID = waitpid(-1, &flag, 0)) != -1) //循环回收子进程,防止僵尸进程出现
{
if (WIFEXITED(flag))
{
printf("这是处理函数,回收的子进程ID是%d。\n", JinCheng_ID);
}
}
}
int main(int argc, char *argv[])
{
pid_t JinCheng_ID; //进程ID
int flag, i;
struct sigaction New_ChuLi_FangShi; //新的处理方式
//创建子进程
for (i = 0; i < 5; i++)
{
JinCheng_ID = fork();
if (JinCheng_ID == 0)
{
break;
}
}
if (i == 5) //父进程
{
New_ChuLi_FangShi.sa_handler = ChuLi_HanShu; //设置处理函数
sigemptyset(&New_ChuLi_FangShi.sa_mask); //清空信号集
New_ChuLi_FangShi.sa_flags = 0; //默认值处理
flag = sigaction(SIGCHLD, &New_ChuLi_FangShi, NULL); //注册信号捕捉函数
if (flag == -1)
{
perror("注册捕捉信号函数失败");
}
printf("这是父进程,进程ID是%d。\n", getpid());
while (1)
{
printf("你好,世界!\n");
sleep(1);
}
}
else //子进程
{
sleep(i);
printf("这是子进程,进程ID是%d,已死亡。\n", getpid());
return i + 1;
}
return 0;
}
测试结果
测试代码7
借助信号捕捉回收子进程,死一个回收一个,同时执行。
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <wait.h>
void ChuLi_HanShu(int XinHao)
{
int flag;
pid_t JinCheng_ID; //进程ID
JinCheng_ID = waitpid(-1, &flag, 0);
if (WIFEXITED(flag))
{
printf("这是处理函数,回收的子进程ID是%d。\n", JinCheng_ID);
}
}
int main(int argc, char *argv[])
{
pid_t JinCheng_ID; //进程ID
int flag, i;
struct sigaction New_ChuLi_FangShi; //新的处理方式
//创建子进程
for (i = 0; i < 5; i++)
{
JinCheng_ID = fork();
if (JinCheng_ID == 0)
{
break;
}
}
if (i == 5) //父进程
{
New_ChuLi_FangShi.sa_handler = ChuLi_HanShu; //设置处理函数
sigemptyset(&New_ChuLi_FangShi.sa_mask); //清空信号集
New_ChuLi_FangShi.sa_flags = 0; //默认值处理
flag = sigaction(SIGCHLD, &New_ChuLi_FangShi, NULL); //注册信号捕捉函数
if (flag == -1)
{
perror("注册捕捉信号函数失败");
}
printf("这是父进程,进程ID是%d。\n", getpid());
while (1)
{
printf("你好,世界!\n");
sleep(1);
}
}
else //子进程
{
sleep(i);
printf("这是子进程,进程ID是%d,已死亡。\n", getpid());
return i + 1;
}
return 0;
}
测试结果
慢速系统调用
可能会使进程永远阻塞的一类,如,read、write、pause、wait。在早期,如果在阻塞期间收到一个信号,该系统调用就被中断,不再继续执行,也可以设定系统调用是否重启。
可修改sa_flags参数来设置被信号中断后系统调用是否重启:
SA_INTERRURT:不重启。
SA_RESTART:重启。
捕捉到信号后,在执行捕捉函数期间,不希望自动阻塞该信号,可将sa_flags设置为SA_NODEFER,除非sa_mask中包含该信号。