Linux信号机制(二)

目录

一、信号的阻塞

二、信号集操作函数

三、sigprocmask函数

四、pause函数 

 五、sigsuspend函数


一、信号的阻塞

        有时候不希望在接到信号时就立即停止当前执行,去处理信号,同时也不希望忽略该信号,而是延时一段时间去调用信号处理函数。这种情况可以通过阻塞信号实现。

        信号的阻塞概念:信号的”阻塞“是一个开关动作,指的是阻止信号被处理,但不是阻止信号产生。

        信号的状态:

        信号递达(Delivery ):实际信号执行的处理过程(3种状态:忽略,执行默认动作,捕获)、信号未决(Pending):从产生到递达之间的状态。

二、信号集操作函数

sigset_t set;  自定义信号集。  是一个32bit  64bit  128bit的数组。

sigemptyset(sigset_t *set);	清空信号集

sigfillset(sigset_t *set);	全部置1

sigaddset(sigset_t *set, int signum);	将一个信号添加到集合中

sigdelset(sigset_t *set, int signum);	将一个信号从集合中移除

sigismember(const sigset_t *set,int signum); 判断一个信号是否在集合中。

三、sigprocmask函数

#include <signal.h>
int sigprocmask( int how, 
                const sigset_t *restrict set, 
                sigset_t *restrict oset );

返回值:若成功则返回0,若出错则返回-1

首先,若oset是非空指针,那么进程的当前信号屏蔽字通过oset返回。

其次,若set是一个非空指针,则参数how指示如何修改当前信号屏蔽字。

how可选用的值:(注意,不能阻塞SIGKILL和SIGSTOP信号)

SIG_BLOCK :   把参数set中的信号添加到信号屏蔽字中
SIG_UNBLOCK: 从信号屏蔽字中删除参数set中的信号
SIG_SETMASK: 把信号屏蔽字设置为参数set中的信号
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>

void handle(int sig)
{
	printf("I get the sig = %d\n",sig);
}

int main()
{
	struct sigaction act;
	act.sa_handler = handle;
	act.sa_flags = 0; 
	sigemptyset(&act.sa_mask);

	sigaction(SIGINT,&act,NULL);
	sigset_t set;
	sigemptyset(&set);
	sigaddset(&set,SIGINT);
	
	sigprocmask(SIG_BLOCK,&set,NULL);

	sleep(5);
	sigprocmask(SIG_UNBLOCK,&set,NULL);
	while(1)
	{
		sleep(1);
	}
	return 0;
}

这段代码注册了一个信号处理函数 handle() 来处理 SIGINT 信号。
然后它创建了一个 sigset_t 类型的信号集 set,并将 SIGINT 添加到这个信号集中。

接着,通过 sigprocmask(SIG_BLOCK, &set, NULL) 调用,程序阻塞了 SIGINT 信号。
这意味着在这个代码块中,SIGINT 信号将被暂时屏蔽,不会触发信号处理函数。

随后,程序调用 sleep(5) 函数来暂停执行 5 秒钟。在此期间,由于 SIGINT 被阻塞,即使用户发送 SIGINT 信号(通常是通过按下 Ctrl+C),信号处理函数 handle() 也不会执行。

然后,通过 sigprocmask(SIG_UNBLOCK, &set, NULL) 调用,解除了对 SIGINT 信号的阻塞。

最后,程序进入一个无限循环,每次循环调用 sleep(1) 函数来保持进程处于活动状态。

 运行结果:

linux@linux:/mnt/hgfs/linuxshare/linux_code/message2$ ./sigmask_new_t
^C^C^C^C^C^C^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^\Quit (core dumped)

四、pause函数 

调用该函数可以造成进程主动挂起,等待信号唤醒。
调用该系统调用的进程将处于阻塞状态(主动放弃cpu) 直到有信号递达将其唤醒。

 int pause(void);     返回值:-1 并设置errno为EINTR
pause() 函数是一个系统调用,它的作用是使当前进程挂起,直到收到一个信号为止。
在收到信号之前,pause() 函数会一直阻塞当前进程。
一旦收到信号,pause() 函数会返回,并且不会执行任何其他代码,直接返回到信号处理函数(如果有的话)或者程序的主体部分。
如下代码中,pause() 函数用于等待SIGINT信号的到来。
一旦收到SIGINT信号(通常由用户在终端上按下Ctrl+C触发),pause() 函数会返回,然后程序会执行信号处理函数handle()。
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>

void handle(int sig)
{
	printf("I get the sig = %d\n",sig);
}

int main()
{
	struct sigaction act;
	act.sa_handler = handle;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);
	sigaction(SIGINT,&act,NULL);

	pause();
	printf("after pause\n");
	while(1)
	{
		sleep(1);
	}
	return 0;
}

注意:第一次CTRL+C会调用handle回调函数且打印after pause,但是第二次CTRL+C后就不会打印after pause。

linux@linux:/mnt/hgfs/linuxshare/linux_code/message2$ ./pause_t
^CI get the sig = 2
after pause
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^CI get the sig = 2
^\Quit (core dumped)
linux@linux:/mnt/hgfs/linuxshare/linux_code/message2$ 

我们用一个测试程序测试一下:

#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>

void handle(int sig)
{
	printf("I get the sig = %d\n",sig);
}

int main()
{
	struct sigaction act;
	act.sa_handler = handle;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);
	sigaction(SIGINT,&act,NULL);

	pause();
	printf("after pause\n");
	while(1)
	{
		printf("test\n");
		sleep(1);
		printf("sleep\n");
	}
	return 0;
}

运行结果:

linux@linux:/mnt/hgfs/linuxshare/linux_code/message2$ ./pause_t
^CI get the sig = 2
after pause
test
sleep
test
sleep
test
sleep
test
sleep
test
sleep
test
sleep
test
^CI get the sig = 2
sleep
test
sleep
test
sleep
test
^CI get the sig = 2
sleep
test
^CI get the sig = 2
sleep
test
sleep
test
^\Quit (core dumped)

可以发现,当我用CTRL+C,接着运行,之后程序就运行到while(1)里了,当我再CTRL+C因为信号捕获的关系才会打印句柄里的语句I get the sig = 2。

而对于如下代码:

        每次CTRL+C都会触发mytask中的语句和handle句柄中的打印语句。

#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>

void handle(int sig)
{
	printf("I get the sig = %d\n",sig);
}

void mytask()
{
	printf("woshigedashabi\n");
}

int main()
{
	struct sigaction act;
	act.sa_handler = handle;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);
	sigaction(SIGINT,&act,NULL);

	pause();
	printf("after pause1\n");
	while(1)
	{
		mytask();
		pause();
	}
	printf("after pasue2\n");
	while(1)
	{
		sleep(1);
	}
	return 0;
}

 运行结果:

linux@linux:/mnt/hgfs/linuxshare/linux_code/message2$ ./test
^CI get the sig = 2
after pause1
woshigedashabi
^CI get the sig = 2
woshigedashabi
^CI get the sig = 2
woshigedashabi
^CI get the sig = 2
woshigedashabi
^CI get the sig = 2
woshigedashabi
^CI get the sig = 2
woshigedashabi
^\Quit (core dumped)

对代码进行一定的修改后:


#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>

void handle(int sig)
{
	printf("I get the sig = %d\n",sig);
}

void mytask()
{
	printf("My task start\n");
	sleep(3);
	printf("My task end\n");
}

int main()
{
	struct sigaction act;
	act.sa_handler = handle;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);
	sigaction(SIGINT,&act,NULL);
	sigaction(SIGHUP,&act,NULL);

	sigset_t set;
	sigaddset(&set,SIGHUP);
	sigaddset(&set,SIGINT);
	pause();
	printf("after pause1\n");

	while(1)
	{
		sigprocmask(SIG_BLOCK,&set,NULL);
		mytask();
		sigprocmask(SIG_UNBLOCK,&set,NULL);
		pause();
	}
/*	while(1)
	{
		mytask();
		pause();
	}*/

	printf("after pasue2\n");
	return 0;
}

运行结果:

第一次CTRL+C触发,打印完after pause1,程序进入while(1)循环,在5s内再按下CTRL+C会被堵塞,直达sigprocmask(SIG_UNBLOCK,&set,NULL);只要在5s内按下了CTRL+C就会信号捕获打印handle中的语句,且这个时候因为pause(),再按下CTRL+C会再次运行mytask()。

linux@linux:/mnt/hgfs/linuxshare/linux_code/message2$ ./test
^CI get the sig = 2
after pause1
My task start
^CMy task end
I get the sig = 2
^CI get the sig = 2
My task start
My task end
^CI get the sig = 2
My task start
My task end
^CI get the sig = 2
My task start
^C^C^C^C^CMy task end
I get the sig = 2
^CI get the sig = 2
My task start
My task end
^CI get the sig = 2
My task start
^C^C^C^C^C^C^C^CMy task end
I get the sig = 2
^CI get the sig = 2
My task start
^C^\Quit (core dumped)

如果上述代码去掉pause(),则输出结果为:则会一直运行mytest(),只是CTRL+C触发运行了handle中的打印语句。

linux@linux:/mnt/hgfs/linuxshare/linux_code/message2$ gcc -o test pause_t_new.c
linux@linux:/mnt/hgfs/linuxshare/linux_code/message2$ ./test
^CI get the sig = 2
after pause1
My task start
My task end
My task start
My task end
My task start
My task end
My task start
^CMy task end
I get the sig = 2
My task start
^C^C^C^C^C^C^C^C^C^CMy task end
I get the sig = 2
My task start
My task end
My task start
My task end
My task start
My task end
My task start
My task end
My task start
^\Quit (core dumped)

 五、sigsuspend函数

int sigsuspend(const sigset_t *sigmask);

功能:将进程的屏蔽字替换为由参数sigmask给出的信号集,然后挂起进程的执行

参数:sigmask:希望屏蔽的信号

对比如下代码:

 运行结果的区别:

左边运行结果表示你在阻塞期间按下CTRL+C只会捕获一次信号,但是不会认为你需要再执行一次mytask()。只有当运行了sigprocmask(SIG_UNBLOCK,&set,NULL)才有效。

但是右边在任务中间会接收任务,这是因为sigsuspend函数,set2是一个空的信号集。sigsuspend(&set2); 函数允许程序在任务执行的过程中等待信号,一旦收到信号,程序就会立即响应。

详细代码如下: 

#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>

void handle(int sig)
{
	printf("I get the sig = %d\n",sig);
}

void mytask()
{
	printf("My task start\n");
	sleep(3);
	printf("My task end\n");
}

int main()
{
	struct sigaction act;
	act.sa_handler = handle;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);
	sigaction(SIGINT,&act,NULL);
	sigaction(SIGHUP,&act,NULL);

	sigset_t set;
	sigset_t set2;
	sigemptyset(&set2);
	sigaddset(&set,SIGHUP);
	sigaddset(&set,SIGINT);
	pause();
	printf("after pause1\n");
	

	while(1)
	{
		sigprocmask(SIG_BLOCK,&set,NULL);
		mytask();
		sigsuspend(&set2);
	}

	printf("after pasue2\n");
	return 0;
}
先注册了两个信号处理函数 handle,分别用于处理 SIGINT 和 SIGHUP 信号
。然后定义了一个自定义函数 mytask(),它模拟了一个长时间运行的任务。

在 main() 函数中,创建了两个信号集 set 和 set2,set 中包含了 SIGHUP 和 SIGINT 信号。
然后调用了 pause() 函数来挂起进程,直到收到信号为止。

接着进入一个无限循环,在循环中,先将 set 中的信号阻塞,然后执行 mytask() 函数,模拟长时间运行的任务。
然后使用 sigsuspend() 函数挂起进程,等待收到 set2 中的信号。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/454405.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

媒体单位专用小记者报名及各类活动报名系统介绍

媒体单位专用小记者报名及各类活动报名系统介绍 小记者活动鼓励孩子们关注生活和社会&#xff0c;丰富成长体验&#xff0c;开启心智&#xff0c;淬砺思想。这不仅有助于提高他们的理性思辨力&#xff0c;还能培养他们的社会责任感和公民意识。小记者活动为学生提供了一个全新…

[密码学]OpenSSL实践篇

背景 最近在写Android abl阶段fastboot工具&#xff0c;需要我在Android代码中实现一些鉴权加解密相关的fastboot命令&#xff0c;里面用到了OpenSSL。我们先来实践一下OpenSSL在Linux系统中的指令。 OpenSSL官方网站&#xff1a;OpenSSL 中文手册 | OpenSSL 中文网 1. 查看…

VMware下创建虚拟机

Centos7是比较常用的一个Linux发行版本&#xff0c;在国内的使用比例比较高 安装完VMware一定要检查虚拟网卡有没有安装成功&#xff0c;如果没有VMnet1和VMnet8 虚拟机是无法上网的&#xff0c;就需要卸载重启电脑重新安装 控制面板—网络和Internet—网络连接 快捷方式打开&a…

【原创】[新增]ARCGIS之土地报备Txt、征地Xls格式批量导出Por旗舰版

一、软件简介 2024年新增旗舰版软件&#xff0c;本软件全新界面开发&#xff0c;保留原有软件功能及一些使用习惯&#xff0c;并集成了现已有的所有定制格式的支持&#xff0c;并增加自定义格式的导出&#xff1b;做到1N2&#xff08;即为1种通用版本N种定制格式导出txt、Xls&a…

牛客Highway

题目大意 在ICPCCamp中&#xff0c;有n个方便编号的城镇&#xff0c;编号为1,2,...,n&#xff0c;它们之间通过&#xff08;n-1&#xff09;条道路相连。连接第i个城镇a_i和b_i的道路的长度为c_i。保证任意两座城市之间只能通过道路到达。 Bobo希望修建&#xff08;n-1&#…

2024-03-13 作业

网络编程&#xff1a; 1.思维导图&#xff1a; 2.上课写的代码&#xff1a; 2.1网络字节序与主机字节序转换 运行代码&#xff1a; #include <myhead.h> int main() {int num 0x12345678;short int value 0x1234;int num_n htonl(num);int value_n htons(value);…

leetcode刷题(javaScript)——分治思想(二分查找、快速排序)相关场景题总结

分治思想是一种将问题分解成更小的子问题&#xff0c;然后解决子问题并将结果合并的算法设计策略。二分查找、快速排序和折半查找都属于分治思想的经典算法。在leetcode里&#xff0c;分治思想一般结合其他场景出现&#xff0c;构成复合型题目。但是在看题时一定要了解能否用分…

【数据结构取经之路】快速排序的非递归实现

概述 递归实现快速排序在一些场景下有栈溢出的风险&#xff0c;下面就谈谈如何用非递归的方法实现快速排序。 非递归实现的思想 递归实现与非递归实现快速排序的本质是一致的&#xff0c;效率并不会因为用了非递归实现而有所提升。递归实现快速排序的本质就在于通过递归&…

MinIO权限提升漏洞CVE-2024-24747详细解决办法

漏洞名称&#xff1a; MinIO权限提升漏洞(CVE-2024-24747) 漏洞简介 2024年2月2日&#xff0c;深瞳漏洞实验室监测到一则MinIO 存在权限提升漏洞的信息&#xff0c;漏洞编号&#xff1a;CVE-2024-24747&#xff0c;漏洞威胁等级&#xff1a;高危。 该漏洞是由于用户创建的访…

【C语言_函数栈帧_复习篇】

目录 一、什么是函数栈帧 二、什么是栈 三、相关寄存器 四、相关汇编命令 五、 解析函数栈帧的创建和销毁 一、什么是函数栈帧 在每一次函数调用之前编译器都会提前在内存的栈区为被调用的函数开辟一块空间&#xff0c;这块空间被称为该函数的函数栈帧&#xff0c;这些空间…

springboot269反欺诈平台的建设

反欺诈平台设计与实现 摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装反欺诈平台软件来发挥其高效地信息处…

使用python实现一个dicom影像解析入库程序demo

简介 DICOM&#xff08;Digital Imaging and Communications in Medicine&#xff09;是医学图像和相关信息的国际标准。它定义了医学影像的格式和通信协议&#xff0c;使得不同设备和系统之间可以交换和共享医学图像和相关数据&#xff0c;如CT扫描、MRI图像、超声波图像等。…

19.创建帖子

文章目录 一、建立路由二、开发CreatePostHandler三、编写logic四、编写dao层五、编译测试运行 一、建立路由 这里要稍微注意的是&#xff1a;需要登录后才可以发表帖子&#xff0c;所以需要用到我们之前写的鉴权中间件。中间件对用户携带的token解析成功后&#xff0c;便会将…

【网络工程师进阶之路】BFD技术

个人名片&#xff1a;&#x1faaa; &#x1f43c;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢AI编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;落798. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&a…

[AutoSar]BSW_Com013 CAN TP 模块配置

目录 关键词平台说明一、缩写对照表二、Functional Description&#xff08;vector&#xff09;2.1 Asynchronous and Synchronous behavior of CanTp_Transmit2.1.1 asynchronous 2.1.2 synchronous2.2 Separation Time by Application 三、CanTpChannels3.1 接收端3.2 发送端…

初识Python语言-课堂练习【pyhton123题库】

初识Python语言-课堂练习【pyhton123题库】 一、单项选择题 1、Guido van Rossum正式对外发布Python版本的年份是&#xff1a; A 2008B 1998C 1991D 2002 【答案】C 【解析】暂无解析2、下面不是Python语言特点的是&#xff1a;‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪…

C++初阶:内存管理

目录 1. C/C中各种资源的内存分布1.1 C/C程序内存区域划分1.2 各资源的内存分布情况&#xff08;练习&#xff09; 2. C中的动态内存管理方式2.1 new/delete开辟内置类型空间2.2 new/delete开辟销毁自定义类型空间 3. operator new 与 operator delete函数4. new与delete的实现…

Spring MVC中的REST风格

文章目录 REST风格1 REST简介问题导入1.1 REST介绍1.2 RESTful介绍1.3 注意事项 2 RESTful入门案例问题导入2.1 快速入门2.2 PathVariable介绍2.3 RequestBody、RequestParam、PathVariable区别和应用 3 REST快速开发【重点】3.1 代码中的问题3.2 Rest快速开发 4案例&#xff1…

算法时空复杂度分析:大O表示法

文章目录 前言大O表示法3个时间复杂度分析原则常见的时间复杂度量级空间复杂度参考资料 前言 算法题写完以后&#xff0c;面试官经常会追问一下你这个算法的时空复杂度是多少&#xff1f;&#xff08;好像作为一名算法工程师&#xff0c;我日常码代码的过程中&#xff0c;并没…

FreMIM:傅里叶变换与遮罩的图像建模在医学图像分割中的应用

代码链接&#xff1a;GitHub - Rubics-Xuan/FreMIM: This repo holds the official code for the paper "FreMIM: Fourier Transform Meets Masked Image Modeling for Medical Image Segmentation". 论文链接&#xff1a;https://arxiv.org/abs/2304.10864 收录于…