Linux——进程信号(下)

目录

总结

一,信号保存

1.1 阻塞信号

2.2 信号在内核(操作系统)中的表示

2.3 系统接口

2.3.1 sigset_t信号集

2.3.2 信号集的操作函数

2.3.3 sigprocmask

 2.3.4 sigpending

2.4 实验样例

三,信号处理

3.1 信号捕捉

 3.2 sigaction接口

3.3 实验样例

 总结


总结

上一篇我们主要讲了进程信号的信号发出,本文主要讲解后两个部分,信号保存和信号处理

一,信号保存

1.1 阻塞信号

在讲解信号是如何保存之前,我们需要先认识一下什么是阻塞信号

首先,在我们进程信号中,实行执行信号的处理动作称为递达 ;而信号从产生到递达的过程叫做未决。而在上面两个过程中,我们可以选择阻塞某个信号,被阻塞的信号会一直处于未决的状态无法递达,直到信号阻塞被解决,信号才会被执行。注意,这里的阻塞和忽略是两码事,阻塞行为是在未决和递达直接,而忽略是递达的一种处理方式。

2.2 信号在内核(操作系统)中的表示

上一遍我们简单讲了一下信号是在内核创建的task_struct中以位图的数据结构表示的,本文就详细讲解一下信号在task_struct中的储存结构

 

 信号在task_struct中的存储结构如上图所示,在结构体中存储的有三个变量block,pending,handler用来表示信号,接下来我们就详细解释一下这三个的变量的含义。

  1. block:32位整形,位图数据结构,其中每个比特位的含义对应的是上面2图中前31个常见的信号,每比特位的内容的含义是有没有阻塞对应信号,1表示阻塞,0表示未阻塞
  2. pending:32位整形,位图数据结构,其中每个比特位的含义对应的是上面2图中前31个常见的信号,每比特位的内容的含义是有没有接收到对应信号,1表示存在,0表示不存在
  3. handler:一个函数指针数组,其中每个下标表示的是图二对应的前31个信号,每个下标的内容则是对应信号的处理方式,SIG_DFL为默认处理方式,SIG_IGN为忽略,函数指针则是自定义处理方式。

当一个信号发送过来时,在储存结构中的流程如下,先在pending中将信号对应比特位置为1,然后在block中检查信号是否被阻塞,如果被阻塞则等待阻塞状态消失再进行处理,否则在合适的时间进行处理,处理时在根据handler中信号对应处理方式进行处理

 上面的task_struct中的信号储存结构,我们需要注意该结构是由内核直接进行管理,而我们用户无法直接插手管理,只能通过内核提供的系统接口控制信号的储存,接下来我们就讲一下内核提供的控制信号储存的系统接口

2.3 系统接口

我们将系统接口主要是先将每个接口的用法含义讲一下,最后在写一个程序用一下这些接口

2.3.1 sigset_t信号集

从上图来看,每个信号只有一个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的。因此,未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,这个类型可以表示每个信号的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有效”和“无效”的含义是该信号是否处于未决状态。下一节将详细介绍信号集的各种操作。 阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask),这里的“屏蔽”应该理解为阻塞而不是忽略。

2.3.2 信号集的操作函数

在Linux下我们可以通过下面的指令查找信号集的操作函数

man sigemptyset

  1. sigemptyset:初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含 任何有效信号。
  2. sigfillset:初始化set所指向的信号集,使其中所有信号的对应bit置位,表示 该信号集的有效信号包括系统支持的所有信号。
  3. sigaddset和sigdelset:指的是增加或者删除set中的某种有效信号
  4. sigismember:检测set中是否由某种信号

注意:在使用sigset_t前必须要用sigemptyset或者sigfillset初始化,使信号集处于确定的初始状态。

这四个函数都是成功返回0,出错返回-1。sigismember是一个布尔函数,用于判断一个信号集的有效信号中是否包含某种 信号,若包含则返回1,不包含则返回0,出错返回-1

2.3.3 sigprocmask

现在我们讲一下如何读取改进进程的信号屏蔽字(阻塞信号集)

 如图想要修改进程的阻塞信号集,需要用系统接口sigprocmask,其中how为修改方式,如果set为非空信号集,则将阻塞信号集set为基地修改阻塞信号集,然后为了保证安全性,会将修改前的阻塞信号集备份放到oldset中传回。how的修改方式有如下几种:

 如果调用sigprocmask解除了对当前若干个未决信号的阻塞,则在sigprocmask返回前,至少将其中一个信号递达。

 2.3.4 sigpending

读取当前进程的未决信号集,通过set参数传出。调用成功则返回0,出错则返回-1。

2.4 实验样例

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
using namespace std;void pendingprint(sigset_t* pendingset)
{int i=1;for(i=1;i<=31;i++){if(sigismember(pendingset,i)==1){cout<<1;}else{cout<<0;}}
}int main()
{sigset_t set,oset;//1.1 初始化sigemptyset(&set);sigemptyset(&oset);//1.2 向屏蔽信号集群加入SIGINT(ctrl+c)sigaddset(&set,SIGINT);//1.3 改进进程的屏蔽信号集sigprocmask(SIG_BLOCK,&set,&oset);while(true){//2.1获取进程的pending信号集sigset_t pendingset;sigpending(&pendingset);//2.2打印pendingset信号集pendingprint(&pendingset);cout<<endl;sleep(1);}return 0;
}

 代码如上所示,主要分两个板块第一个就是对信号集完成初始化并且添加SIGINT信号,然后修改进程的屏蔽信号集;第二步是打印观察进程的pending信号集,主要观察当我们发送SIGINT前后的变化

 结果如上,我们发现当我们发送SIGINT信号号,其确实由于阻塞信号集而一直处于未决的阶段

三,信号处理

首先,信号处理并不是立即处理,而是寻找合适的时机进行处理。为什么呢?因为信号的产生是异步的,当信号发送过来时进程可能在处理更重要的事情,会等待合适的时机处理信号。那么合适的时机又是什么呢?指的是当进程从内核态切换到用户态时,进程会在内核的指导下,进行信号的检测以及处理。

 那么什么是内核态和用户态呢?

首先,用户态指运行用户自己的代码时的状态;内核态指的是发生时钟中断或者调用系统接口是会由用户态转变为内核态。由于内核态讲起来比较复杂,因此我们这里大致讲一下,在正常的32位4G的进程地址空间中,前3G属于用户区,存储的是堆栈代码数据等,3G-4G的空间存储的是操作系统的代码和数据,而CPU中会有一个专门的寄存器记录进程的状态,0是内核态,3是用户态。

用户态转为内核态主要有两种方式:

  1. 每隔一段时间硬件会向内核发送时钟中断,此时会由用户态转为内核态,内核态切换进程也是在这个时候
  2. 系统调用,当调用系统接口时,会转化为内核态调用接口

3.1 信号捕捉

如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。而信号的捕捉具体方式如下图

在上图中我们以信号SIGHA为自定义处理方式为例子,在执行某条代码时,可能会因为时钟中断,异常,系统调用等行为进入内核处理,当内核处理完准备回用户模式之前,会检测当前过程中可以递送的信号,如图SIGHA的处理方式为自定义,当要处理SIGHA时会回到用户模式调用SIGHA的自定义处理函数,处理后会调用sigreturm再次回到内核,然后在返回用户模式之前会再次检测当前进程中是否有可以递送的信号,并重复上面的行为

 3.2 sigaction接口

 sigaction函数可以读取和修改与指定信号相关联的处理动作。调用成功则返回0,出错则返回- 1。signo是指定信号的编号。若act指针非空,则根据act修改该信号的处理动作。若oact指针非 空,则通过oact传出该信号原来的处理动作。sigaction结构体具体如下:

 

 在该结构体中,sa_handler为制定编号所自定义的处理方式,sa_mask则是在处理过程中需要屏蔽的额外的信号,为什么有sa_mask呢?我们需要了解到,当我们在处理一个信号时,进程会将该信号自动加入进程的阻塞集中,这样就保证了在处理这个信号时,即使又接收到了这个信号,它也会被阻塞到该信号处理结束为止。

3.3 实验样例

#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
using namespace std;void pendingprint(sigset_t* pendingset)
{int i=1;for(i=1;i<=31;i++){if(sigismember(pendingset,i)==1){cout<<1;}else{cout<<0;}}
}void handler(int signal)
{int i=0;//2.2 定时15秒后结束处理观察发生什么while(i<15){//2.3 打印进程的pending信号集sigset_t pendingset;sigpending(&pendingset);pendingprint(&pendingset);cout<<endl;i++;sleep(1);}
}
int main()
{//1.1 创建结构体act oldactstruct sigaction act;struct sigaction oldact;//1.2 将act中的处理方式改为自定义处理方式act.sa_handler=handler;act.sa_flags=0;//1.3添加额外的需要阻塞的信号3,4,5sigaddset(&act.sa_mask,3);sigaddset(&act.sa_mask,4);sigaddset(&act.sa_mask,5);//2.1 修改2信息号的处理方式为自定义处理sigaction(2,&act,&oldact);while(true){cout<<getpid()<<endl;sleep(1);}return 0;
}

代码如上图,我们主要修改2信号的处理方式为handler,handler主要是在15秒内一直打印pending信号集,我们可以借此观察在处理信号时对sa_mask中信号的阻塞效果,以及处理结束后的情况,结果如下

 

 如图,当我们运行后发送信号2,我们看到进程循环打印pending信号集,此时当我们发送信号3,4我们发现由于我们把其加入了阻塞信息集,导致2,3,4信号都被阻塞,当15秒后handler结束,我们发现信号阻塞消失,信号由操作系统处理

 总结

linux的进程信号到这里就结束了,希望铁子们能够有所收货。

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

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

相关文章

c++11 标准模板(STL)(std::basic_istream)(一)

定义于头文件 <istream> template< class CharT, class Traits std::char_traits<CharT> > class basic_istream : virtual public std::basic_ios<CharT, Traits> 类模板 basic_istream 提供字符流上的高层输入支持。受支持操作包含带格式…

3D空间的旋转的各种等价形式

旋转矩阵 欧拉轴和角度&#xff08;旋转矢量&#xff09; 欧拉旋转 四元数

Linux的

&#xff08;该图由AI绘制 关注我 学习AI画图&#xff09; 目录 网络配置 1、ifconfig查看网络信息 2、与网卡相关的配置文件 3、查询计算机的网络状态 4、systemctl启动/重启/停止网络 Linux远程连接与文件传输 1、为什么需要远程连接 2、SSH协议 3、sshd服务 4、…

​如何优雅的卸载Edge浏览器

如何优雅的卸载Edge浏览器 由于Edge浏览器越来越复杂&#xff0c;功能越来越繁琐我是真的一刻也用不下去了。虽然我主力是火狐浏览器&#xff0c;Edge用来访问一些只能使用Chromium内核的网页作为备用。 但是我现在一打开Edge浏览器我就窝火&#xff0c;也懒得再去调整优化&a…

Golang每日一练(leetDay0116) 路径交叉、回文对

目录 335. 路径交叉 Self-crossing &#x1f31f;&#x1f31f;&#x1f31f; 336. 回文对 Palindrome Pairs &#x1f31f;&#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Rust每日一练 专栏 Golang每日一练 专栏 Python每日一练 专栏 C/…

【Java遇错】Error: failed to initialize Sentinel CommandCenterLog

问题描述&#xff1a; 引入sentinel的相关依赖之后&#xff0c;启动项目服务&#xff0c;发现如下错误 Error: failed to initialize Sentinel CommandCenterLog java.lang.NoClassDefFoundError: com/alibaba/csp/sentinel/log/LoggerSpiProviderat com.alibaba.csp.sentin…

Python应用实例(一)外星人入侵(五)

外星人入侵&#xff08;五&#xff09; 1.项目回顾2.创建第一个外星人2.1 创建Alien类2.2 创建Alien实例 3.创建一群外星人3.1 确定一行可容纳多少个外星人3.2 创建一行外星人3.3 重构_create_fleet()3.4 添加行 在游戏《外星人入侵》中添加外星人。我们将首先在屏幕上边缘附近…

Cannot find tomcat-9.0.0.M21/bin/setclasspath.sh

问题描述&#xff1a;将linux上的tomcat直接拷贝到以一个路径下&#xff0c;执行sh startup.sh 报错 解决&#xff1a;修改全局变量配置文件 1、vim /etc/profile &#xff08;主要修改如下图所标记的值 &#xff09; 2、source /etc/profile &#xff08;设置环境变量立即…

C. Vampiric Powers, anyone? - 思维+前缀和

分析&#xff1a; 添加新元素的操作可以理解为添加任意一段以n结尾的异或和&#xff0c;当原数组总异或和与新加的元素进行异或又可以得到剩余的前缀的异或和&#xff0c;假设新加的元素的值是i到n的异或和x&#xff0c;那么总异或和sumpre^x&#xff0c;所以sum^xpre&#xff…

ChatGPT 话题相关和类 ChatGPT 工具 | 优质文章、相关论文、应用、学习资源整理

文章目录 一、前言二、主要内容三、总结 &#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 一、前言 人工智能与手机和互联网一样具有革命性。 2023 年已经过去一半&#xff0c;ChatGPT 在今年以来一直备受瞩目。目前 ChatGPT 的更新速度逐渐放缓&#…

Spring Boot的Maven插件Spring Boot Maven plugin详解

1.Spring Boot的Maven插件Spring Boot Maven plugin详解 2.Maven插件之git-commit-id-plugin

Devops系列五(CI篇之pipeline libraray)jenkins将gitlab helm yaml和argocd 串联,自动部署到K8S

一、说在前面的话 本文是CI篇的上文&#xff0c;因为上一篇已经作了总体设计&#xff0c;就不再赘述&#xff0c;有需要的请看前文。 我们将演示&#xff0c;使用CI工具–jenkins&#xff0c;怎么和CD工具–argocd串联&#xff0c;重点是在Jenkins该怎么做。准备工作和argocd等…