Linux信号机制(二)

news2024/11/16 20:41:59

目录

一、信号的阻塞

二、信号集操作函数

三、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 中的信号。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【闲聊】-后端框架发展史

框架&#xff0c;是为了解决系统复杂性&#xff0c;提升开发效率而产生的工具&#xff0c;主要服务于研发人员。 当然&#xff0c;框架还有更深层的作用&#xff0c;框架的沉淀是一种高级的抽象&#xff0c;会将人类的业务逐步抽象为统一标准又灵活可变的结构&#xff0c;为各行…

Java-CAS 原理与 JUC 原子类

由于 JVM 的 synchronized 重量级锁涉及到操作系统&#xff08;如 Linux&#xff09; 内核态下的互斥锁&#xff08;Mutex&#xff09;的使用&#xff0c; 其线程阻塞和唤醒都涉及到进程在用户态和到内核态频繁切换&#xff0c; 导致重量级锁开销大、性能低。 而 JVM 的 synchr…

影响汇率的因素?fpmarkets澳福总结几个

汇率对于刚刚开始外汇交易的新手来说非常重要&#xff0c;这不是没有道理的&#xff0c;了解汇率如何变化以及怎么变化有助于在外汇交易中获得稳定的利润。那么影响汇率的因素有哪些&#xff1f;fpmarkets澳福总结几个。 任何国家货币的汇率都是由市场决定的。主要的市场因素是…

汽车网络基础知识 要点

在以太网开发中&#xff0c;常常会听到一些专业名词&#xff0c;例如PHY&#xff0c;MAC&#xff0c;MII&#xff0c;switch&#xff0c;下面是解释 PHY PHY 是物理接口收发器&#xff0c;它实现物理层。包括 MII/GMII (介质独立接口) 子层、PCS (物理编码子层) 、PMA (物理介…

高颜值HMI触控界面一出,价值感飙升,瞬间感觉消费不起了。

千万不要觉得用户很理性&#xff0c;其实用户都是“好色之徒”&#xff0c;判断产品价值基本上靠眼睛&#xff0c;颜值高的价格高&#xff0c;质量高&#xff0c;反之质量低&#xff0c;价格低。如果通过精心的高颜值设计&#xff0c;能让你的产品价值感拉满&#xff0c;你不心…

html密码访问单页自定义跳转页面源码

内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 密码访问单页自定义跳转页面&#xff0c;修改了的密码访问单页&#xff0c;添加了js自定义密码跳转页面。需要正确输入密码才能跳转目标网址。 二、效果展示 1.部分代码 代码如下&…

等保测评的知识

结合自己所学的知识和网络上的一些知识做个小总结。 目录 一、概念&#xff1a; 二、等级划分&#xff1a; 三、技术要求&#xff1a; 四、管理要求&#xff1a; 五、等保测评实施过程&#xff1a; 六、典型的网络架构&#xff1a; 一、概念&#xff1a; 全称为信息安全等级保…

Vue2(五):收集表单数据、过滤器、自定义指令、Vue的生命周期

一、收集表单数据 爱好&#xff1a;学习<input type"checkbox" value"study" v-model"hobby">打游戏<input type"checkbox" value"games" v-model"hobby">吃饭<input type"checkbox" v…

创建局域网分享图片及html页面服务(简单讲下)

目录 1. 使用Python的SimpleHTTPServer&#xff08;适用于Windows&#xff09; 打开其中的.html文件&#xff1a; 打开其中的.png文件&#xff1a; 推荐第2种&#xff1a; 2. 使用Node.js和http-server&#xff08;适用于所有平台&#xff09; 安装http-server&#xff08;…

PSCA复位控制集成之复位信号

组件可能支持两种基本的复位类型。 • 冷复位&#xff1a;重置组件中的所有逻辑。用作上电复位。 • 热复位&#xff1a;重置组件中的大部分逻辑。通常&#xff0c;复位的范围是所有功能逻辑。不包括在热复位中的逻辑会随组件类型而变化&#xff0c;但通常会排除诸如调试和 R…

【论文阅读】Scalable Diffusion Models with Transformers

DiT&#xff1a;基于transformer架构的扩散模型。 paper&#xff1a;[2212.09748] Scalable Diffusion Models with Transformers (arxiv.org) code&#xff1a;facebookresearch/DiT: Official PyTorch Implementation of "Scalable Diffusion Models with Transformer…

13 秒插入 30 万条数据,这才是 Java 批量插入正确的姿势!

本文主要讲述通过MyBatis、JDBC等做大数据量数据插入的案例和结果。 30万条数据插入插入数据库验证 实体类、mapper和配置文件定义 User实体 mapper接口 mapper.xml文件 jdbc.properties sqlMapConfig.xml 不分批次直接梭哈 循环逐条插入 MyBatis实现插入30万条数据 J…

python 深度学习 记录遇到的报错问题12

本篇继python 深度学习 记录遇到的报错问题11_undefined symbol: __nvjitlinkadddata_12_1, version-CSDN博客 目录 一、AttributeError: module ‘tensorflow‘ has no attribute ‘app‘ 二、AttributeError: module tensorflow has no attribute placeholder 三、Attribu…

基于51单片机火灾报警器设计

一、系统方案 1、本设计采用51单片机作为主控器。 2、液晶1602显示。 3、采集温度值&#xff0c;烟雾值。 4、按键设置温度、烟雾报警值&#xff0c;测量值超过设置值蜂鸣器报警。 5、按键布防&#xff0c;有人闯入&#xff0c;声光报警。 二、硬件设计 原理图如下&#xff1a…

牛客DP34 前缀和

解题思路 题目解析如图 思路 算出每个位置的到第一个位置的总和 比如 第一个位置 1 总和 1 第二个位置 2 总和 3 第三个位置 4 总和 7 要算 2到3 位置的前缀和 用3位置的总和减去1位置的总和即可 还要处理一个边界情况 如果1到1位置的前缀和那么就是 …

【React】Vite创建React+TS项目

前提条件 有node环境&#xff0c;且node版本>18.0.0 创建项目 npm create vitelatest1.起项目名 2.选择框架 3.选择语言 TypeScript SWC 是指 Vite 使用 SWC&#xff08;Speedy Web Compiler&#xff09;作为 TypeScript 的编译器。 SWC 是一个针对 JavaScript 和 Ty…

HarmonyOS NEXT应用开发之SideBarContainer侧边栏淡入淡出动效实现案例

介绍 在2in1或平板上&#xff0c;群聊侧边栏是一种较为常用的功能&#xff0c;虽然HarmonyOS已经具备了基本的动效&#xff0c;但是部分情况下开发者可能有定制侧边栏动效的需求&#xff0c;本例主要介绍了如何基于显式动画实现侧边栏的淡入淡出动效。 效果图预览 使用说明&a…

力扣模板题:检测字符串中数字是否递增

bool areNumbersAscending(char * s){//双指针操作&#xff0c;前指针保存前面一个数字字符int p0,q0;for(int i0;s[i];i){if(s[i]>0&&s[i]<9){pp*10s[i]-0;if(s[i1] ||s[i1]\0){//进行比较, 比较过后将p赋值q&#xff0c;q记录前面一个数字,因为数字均为小于100…

Git 仓库瘦身与 LFS 大文件存储

熟悉 Git 的小伙伴应该都知道随着 Git 仓库维护的时间越来越久&#xff0c;追踪的文件越来越多&#xff0c;git 存储的 objects 数量会极其庞大&#xff0c;每次从远程仓库 git clone 的时候都会墨迹很久。如果我们不小心 git add 了一个体积很大的文件&#xff0c;且 git push…

通俗易懂的精度Precision和召回率Recall解释,看这篇就行,5分钟记住。

一、背景 因为我是做机器人方向的&#xff0c;不可避免的涉及到视觉方向的内容&#xff0c;还有审稿的时候也会看到识别相关的内容&#xff0c;其中衡量识别效果的指标包括精度Precision和召回率Recall&#xff0c;虽然很好理解&#xff0c;但每次都记不住&#xff0c;趁这次机…