Linux信号机制(二)

目录

一、信号的阻塞

二、信号集操作函数

三、sigprocmask函数

四、pause函数 

 五、sigsuspend函数


一、信号的阻塞

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

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

        信号的状态:

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

二、信号集操作函数

sigset_t set;  自定义信号集。  是一个32bit  64bit  128bit的数组。sigemptyset(sigset_t *set);	清空信号集sigfillset(sigset_t *set);	全部置1sigaddset(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.hqwc.cn/news/537541.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Stable Diffusion WebUI 1.8.0来了

上周 Stable Diffusion WebUI 发布了 1.8.0 版本&#xff0c;更新内容比较多&#xff0c;据说显存使用有了大幅的下降&#xff0c;这几天我也找时间把 AutoDL 镜像的版本做了个升级&#xff0c;有兴趣的同学可以去体验下新版本了。 这里分享下其中几个我认为比较重要的更新。 …

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

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

打卡学习kubernetes——kubernetes架构原理

接上一篇的内容&#xff0c;除了核心组件&#xff0c;还有一些推荐的Add-ons&#xff1a; kube-dns 负责为整个集群提供DNS服务Ingress Controller 为服务提供外网入口Heapster 提供资源监控&#xff08;没用过这个&#xff0c;但是用过grafana&#xff0c;很方便&#xf…

【已解决】由于启动计算机时出现了页面文件配置問題,Windows在你的计算机 人上创建了一个临时页面文件

最近装了系统&#xff0c;重启电脑时候会有这个问题&#xff1a; 解决方法&#xff1a; 设置虚拟内存的步骤如下&#xff1a; 第一步&#xff1a;用鼠标右键单击桌面上的“计算机”图标&#xff0c;在弹出的快捷菜单中选择“属性”。 第二步&#xff1a;单击系统属性窗口里的“…

Unix环境高级编程-学习-05-TCP/IP协议与套接字

目录 一、概念 二、TCP/IP参考模型 三、客户端和服务端使用TCP通信过程 1、同一以太网下 四、函数介绍 1、socket &#xff08;1&#xff09;声明 &#xff08;2&#xff09;作用 &#xff08;3&#xff09;参数 &#xff08;4&#xff09;返回值 &#xff08;5&…

【Python/crawl】如何使用Python爬虫将一系列网页上的同类图片下载到本地

【需求】 从网页https://www.zhainq.com/%e7%be%8e%e5%a5%b3%e5%86%99%e7%9c%9f%e6%9c%ba%e6%9e%84/%e6%97%a5%e6%9c%ac%e7%be%8e%e5%a5%b3%e5%86%99%e7%9c%9f/109012.html 开始&#xff0c;有十七页&#xff0c;每页都有大漂亮“小濑田麻由”的若干图片&#xff0c;想要将其…

HIVE伪分布安装

引言 Hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张表,类似于RDBMS(关系型数据库,如MySQL、Oracle、PgSQL),并提供类SQL的查询功能。 实验准备 1.搭建好伪分布安装模式的Hadoop的虚拟机,并配置了Linux网络。(可看我前面发布的文章) 2.apache…

使用 java 发送邮件,附件展示乱码,不是展示自己定义的文件名

刚开始我写的代码是这样的 private Mail initMail(List<Map<String , String>> writeInFieldsList , List<Map<String , ?>> allFieldsList , String smallCity) throws Exception {byte[] excelBytes CustomExcelUtils.trafficSignalWarning(writeI…

《IAB视频广告标准:综合指南(2022)》之概述篇 - 我为什么要翻译介绍美国人工智能科技公司IAB 系列(2)

IAB平台&#xff0c;使命和功能 IAB成立于1996年&#xff0c;总部位于纽约市。 作为美国的人工智能科技巨头社会媒体和营销专业平台公司&#xff0c;互动广告局&#xff08;IAB- the Interactive Advertising Bureau&#xff09;自1996年成立以来&#xff0c;先后为700多家媒体…

完整的通过git命令框和windows窗口将本地文件上传到gitee远程仓库流程步骤

1.下载git 这个网站搜索git官方&#xff0c;去下载就行了 2.打开git安装后的Git Bash命令框 3.在Git Bash命令框设置一下要远程链接的gitee账号 git config --global user.name “名字”Git config --global user.email “邮箱” 4.查看一下账号设置 git config --global -…

Android14之解决报错:No module named sepolgen(一百九十二)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

Transformer家族

在《Transformer原理》中我们介绍了&#xff0c;现在很多大模型都是基于Transformer&#xff0c;其中最出名就是GPT和BERT模型&#xff0c;在GPT和BERT模型被提出来之后&#xff0c;NLP领域也出现了基于Transformer结构的模型&#xff0c;按照模型结构基本可以分为三类&#xf…