【Linux】进程信号的捕捉处理

在这里插入图片描述

个人主页~


进程信号的捕捉处理

  • 一、信号捕捉处理的概述
    • 1、信号捕捉处理全过程
    • 2、用户态和内核态的区别
      • (一)用户态
      • (二)内核态
      • (三)用户态与内核态的切换
      • (四)硬件条件
  • 二、再谈进程地址空间
    • 操作系统本质
  • 三、系统调用函数
  • 四、其他补充内容
    • 1、可重入函数
    • 2、volatile关键字

一、信号捕捉处理的概述

1、信号捕捉处理全过程

在这里插入图片描述
如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号,这个我们前面说过,但是我们的过程是比较复杂的,首先我们在执行主控制流程的某条指令时因为系统调用等原因会进入内核,然后内核处理完成后发送信号,如果信号的处理动作是自定义的信号处理函数就回到用户区执行信号处理函数,执行完之后因为信号处理函数的特殊性,它要再次进入内核区,然后回到用户模式继续执行

我们在用户区和内核区来回切换的时候,操作系统负责做我们的身份(用户身份和内核身份)切换工作,用户态陷入内核态是通过汇编指令int 80完成的

在进程从内核态返回用户态时进行信号的检测和处理

并且main函数和自定义信号捕捉处理函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系

2、用户态和内核态的区别

用户态和内核态是操作系统中CPU的两种运行状态,它们在访问权限、资源使用等方面存在显著差异,以下是对这两种状态的标准解释:

(一)用户态

用户态是操作系统为普通用户程序提供的一种运行模式,在用户态下运行的程序拥有较低的特权级别,只能访问受限的系统资源和执行特定的操作,大部分用户编写的应用程序(如文本编辑器、浏览器等)都是在用户态下运行的

用户态程序只能访问自己的内存空间,不能直接访问系统的核心资源,如硬件设备、操作系统内核的数据结构等,并且只能执行一部分指令,一些具有高风险或对系统影响较大的指令(如修改系统时钟、控制硬件中断等)是被禁止执行的
由于用户态程序的权限受到限制,即使程序出现错误(如内存越界、死循环等),也不会对整个操作系统造成严重影响,只会影响到该程序自身

(二)内核态

内核态是操作系统内核运行的模式,具有最高的特权级别,操作系统内核负责管理系统的核心资源,如内存、进程、文件系统、设备驱动等,因此需要在高特权的内核态下运行

内核态程序可以访问系统的所有资源,包括硬件设备、内核数据结构、所有进程的内存空间等,并且可以执行所有的 CPU 指令,包括那些在用户态下被禁止执行的特权指令,这些特权指令可以用于实现系统的关键功能,如进程调度、内存管理、中断处理等
由于内核态程序具有最高的权限,一旦内核态程序出现错误,可能会导致整个操作系统崩溃或出现严重的系统故障

(三)用户态与内核态的切换

在操作系统的运行过程中,程序需要在用户态和内核态之间进行切换,以完成不同的任务,常见的切换场景包括:

  • 系统调用:当用户态程序需要访问系统资源或执行特权操作时,会通过系统调用(如 openreadwrite 等)请求操作系统内核的服务,在执行系统调用时,程序会从用户态切换到内核态,由操作系统内核来处理请求,处理完成后再切换回用户态
  • 中断处理:当系统发生硬件中断(如时钟中断、键盘中断等)或软件中断(如异常、陷阱等)时,CPU 会自动从用户态切换到内核态,由操作系统内核来处理中断事件,处理完成后,根据情况决定是否切换回用户态

但是操作系统是不相信用户的,所以用户态和内核态进行切换的时候是操作系统完成的

(四)硬件条件

在 CPU 中有一个ecs寄存器,它的后两个bit位就标记了当前是处于用户态还是内核态,其中 00 表示处于内核态,11 表示处于用户态,int 80 指令本质上就是将 11 修改成 00

二、再谈进程地址空间

在这里插入图片描述
我们再来拿出我们的老图,我们知道,每个进程都有一个PCB,然后PCB中有一个结构体叫做mm_struct,又被称为进程地址空间,就是我们常说的虚拟地址,虚拟地址通过页表和MMU映射到物理内存上,可以说页表就是虚拟内存和物理内存之间的桥梁,每个进程都会有一个页表,但是,这是对于用户区空间来说的,我们的内核空间,实际上也是有一个内核页表,这个内核页表是唯一的,内核空间中的数据也是唯一的,我们不同的进程,它们用户区空间的代码和数据不同,但是内核空间的数据一定相同,所有进程共享一份内核空间以及一份内核页表

对于进程来说,去调用系统调用接口,就是在我自己的地址空间中执行的,对于操作系统来说,任何一个时刻都有进程执行,我们想执行操作系统的代码就可以随时执行

操作系统本质

操作系统的本质就是一个基于时钟中断的一个死循环,这里的时钟中断与STM32中的《TIM定时器》(学习STM32的可以点开看一下,没有学习就算了,不影响理解)类似,都是每隔很短的时间向计算机发送时钟中断,操作系统在收到时钟中断后,就去中断向量表中执行相应的进程调度之类的方法

三、系统调用函数

sigaction用于设置或检查信号处理行为的系统调用函数

#include <signal.h>
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact); 

返回值:成功返回0,失败返回-1
signo:需要处理的信号编号
act:输入型参数,指向struct sigaction结构体,用于指定新的信号处理动作
oact:输出型参数,指向struct sigaction结构体,用于保存原来的信号处理动作

其中结构体sigaction是这样的

struct sigaction {void   (*sa_handler)(int); // 信号处理函数指针,或SIG_IGN(忽略信号)、SIG_DFL(使用默认处理)void   (*sa_sigaction)(int, siginfo_t *, void *); //另一种信号处理函数指针,用于处理带附加信息的信号sigset_t sa_mask;           //在信号处理函数执行期间需要阻塞的信号集int      sa_flags;          //控制信号处理行为的标志位void   (*sa_restorer)(void); //已弃用,通常设为 NULL
};

其中我们主要研究两个成员:sa_handlersa_mask

这里我们说一个结论,就是信号处理函数在处理信号时,是不再接受新的信号的,在该信号处理函数被调用时,在刚要调用的某个时间,内核会自动将当前信号加入到进程的信号屏蔽字中,这就可以实现在处理某个信号的过程中,不会被这个信号反复打扰,如果这个信号再次产生,它会被阻塞到当前函数处理结束,如果在当前信号被阻塞以外,还想要屏蔽其他的一些信号,则动用sa_mask说明要额外屏蔽的信号

#include <iostream>
#include <signal.h>
#include <cstring>
#include <unistd.h>using namespace std;void PrintPending()
{sigset_t set;//将当前被阻塞且处于未决状态的信号集存储到set信号集中(输出型参数)sigpending(&set);//打印set位图(即pending位图)for (int signo = 31; signo >= 1; signo--){if (sigismember(&set, signo))cout << "1";elsecout << "0";}cout << endl;
}void handler(int signo)
{//收到2号信号打印一句收到了,然后循环调用PrintPendingcout << "catch a signal, signal number : " << signo << endl;while (true){PrintPending();sleep(1);}
}int main()
{//创建并初始化act和oactstruct sigaction act, oact;memset(&act, 0, sizeof(act));memset(&oact, 0, sizeof(oact));//设置sa_mask为全0sigemptyset(&act.sa_mask);//设置信号屏蔽sigaddset(&act.sa_mask, 1);sigaddset(&act.sa_mask, 3);sigaddset(&act.sa_mask, 4);//设置信号处理函数为handleract.sa_handler = handler; // SIG_IGN SIG_DFL//需要处理的就是2号信号sigaction(2, &act, &oact);while (true){cout << "I am a process: " << getpid() << endl;sleep(1);}return 0;
}

在这里插入图片描述
在这里插入图片描述
正常情况下,我们没有发送任何信号,1号信号会将进程终止,当我们发送2号信号,sigaction函数将信号捕捉后,我们进入到handler函数,开始打印全0的pending位图,此时属于信号处理函数处理信号的过程,然后我们发送1234号信号,因为134号信号我们提前设置进sa_mask当中了,所以我们在发送信号的时候对应的比特位就会由0置1,表示阻塞通过

四、其他补充内容

1、可重入函数

在这里插入图片描述
这是一个正常的单链表,node1node2是两个待插入节点,其中,我们要头插node1,其方法就是将next指针指向现在的头节点,再将自己赋值给头节点

insert(struct Node* node)
{node->next = head;head = node;
}

但是在node1->next = head;执行完毕后,还没来得及执行head = p;突然来了一个信号,这个信号刚好被捕捉了,执行自定义动作,刚好自定义动作是将node2头插

void handler(int signo)
{insert(&node2);
}
insert(struct Node* node)
{node->next = head;head = node;
}

执行完这个信号处理函数后再返回执行main函数中的代码
这样一来我们实际上的代码就变成了

node1->next = head;
node2->next = head;
head = &node2;
head = &node1;

在这里插入图片描述
这样上面node1是头插进去了,但是node2是不可能通过单链表的接口访问到了,这个节点就丢失了,这样就会导致内存泄漏

如果一个函数像上面一样被重复进入的情况下,出错或者可能出错,就叫做不可重入函数,否则就叫做可重入函数,我们学习到的大部分函数都是不可重入函数,因为只要是涉及指针改指向的问题,基本上都是不可重入的

2、volatile关键字

volatile是一个类型修饰符,用于告知编译器它修饰的内容拒绝优化

该程序在收到2号信号之前一直在while循环中啥也不干,收到2号信号执行handler打印,设置flag为1,然后继续while判断,从这里开始就变得不一样了
在这里插入图片描述
我们可以看到我们编译的方法加了-O1选项,这个叫做优化,一共有-O0 -O1 -O2 -O3四种种优化等级,其中O0是无优化
在这里插入图片描述

这是加了volatile的关键词产生的结果,按下ctrl+c打印执行后,flag置1,while判断继续向后执行,打印,程序结束
在这里插入图片描述
这是不加volatile的关键词产生的结果,按下ctrl+c打印执行后,flag置1,怎么不往下执行了呢,我再次按下ctrl+c打印再次执行
在这里插入图片描述
这里就是优化的问题,优化其实是CPU管理资源的一种方式,优化后,CPU在第一次读取flag的时候,将其加载到CPU寄存器中,handler结束后进行while的判断时,直接判断CPU上的这个flag,不再去物理内存当中寻找,因为这样的寻找是耗费资源的,加了volatile关键字后,不再产生优化,在判断时判断的就是物理内存中的flag

所以,涉及到要随时了解某些变量的状态的时候,这些变量在优化的时候最好用volatile修饰一下


今日分享就到这了~

在这里插入图片描述

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

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

相关文章

Nyquist内置函数-概述

1 Nyquist内置函数-概述 本章提供奈奎斯特&#xff08;Nyquist&#xff09;语言参考。操作按功能和抽象级别分类。奈奎斯特在两个重要级别上实现&#xff1a;“高级”级别支持行为抽象&#xff0c;这意味着像 stretch 和 at 这样的操作可以应用。这些函数是典型用户期望使用的…

数据驱动防灾:AI 大模型在地质灾害应急决策中的关键作用。基于DeepSeek/ChatGPT的AI智能体开发

全球气候变化加剧了滑坡、泥石流等地质灾害的发生频率与不确定性&#xff0c;传统基于统计与物理模型的预测方法常受限于‌数据稀疏性‌与‌动态耦合复杂性‌。近年来&#xff0c;AI智能体&#xff08;AI Agents&#xff09;与大型语言模型&#xff08;LLMs&#xff09;的突破为…

光谱相机在工业中的应用

光谱相机&#xff08;多光谱、高光谱、超光谱成像技术&#xff09;在工业领域通过捕捉物质的光谱特征&#xff08;反射、透射、辐射等&#xff09;&#xff0c;结合化学计量学与人工智能算法&#xff0c;为工业检测、质量控制和工艺优化提供高精度、非接触式的解决方案。以下是…

Dify工作流中如何去除deepseek-r1思考内容

在工作流中deepseek-r1的think标签内部的内容&#xff0c;很容易让工作流其他的llm产生幻觉&#xff0c;导致不能良好的生成目标效果。 我们通过代码的方式让deepseek-r1既有think思考链的效果&#xff0c;又不传递思考链。 工作流的逻辑为上图 去除think中的代码为 import re…

容器的CPU

1、限制进程的CPU 通过Cgroup来限制进程资源的使用&#xff0c;CPU Cgroup 是 Cgroups 其中的一个 Cgroups 子系统&#xff0c;它是用来限制进程的 CPU 使用的。 cpu.cfs_period_us&#xff0c;它是 CFS 算法的一个调度周期&#xff0c;一般它的值是 100000&#xff0c;以 mic…

【系统分析师-第二篇】

学习目标 通过参加考试&#xff0c;训练学习能力&#xff0c;而非单纯以拿证为目的。 1.在复习过程中&#xff0c;训练快速阅读能力、掌握三遍读书法、运用番茄工作法。 2.从底层逻辑角度理解知识点&#xff0c;避免死记硬背。 3.通过考试验证学习效果。 学习方法 第二遍快速…

【再探图论】深入理解图论经典算法

一、bellman_ford 1. 是什么松弛 在《算法四》中&#xff0c;对松弛的解释是&#xff1a;relax the edge&#xff0c;看起来比较抽象&#xff0c;不过如果我们从生活中的实例去理解&#xff0c;就简单多了&#xff1a; 试想一根绳索&#xff0c;当你握着绳索的两头使劲用力拉…

基于pycharm的YOLOv11模型训练方法

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、前期准备1.1 软件环境配置1.2 训练集参考 二、训练步骤2.1 打开文件夹2.2 打开文件2.3 data.yaml最终代码 三、train.py四、最终结果五、detect.py六、 拓展…

用nodejs连接mongodb数据库对标题和内容的全文本搜索,mogogdb对文档的全文本索引的设置以及用node-rs/jieba对标题和内容的分词

//首先我们要在Nodejs中安装 我们的分词库node-rs/jieba,这个分词不像jieba安装时会踩非常多的雷&#xff0c;而且一半的机率都是安装失败&#xff0c;node-rs/jieba比jieba库要快20-30%&#xff1b;安装分词库是为了更好达到搜索的效果 这个库直接npm install node-rs/jieba即…

水下声呐探测仪,应急救援中的高效水下定位技术|深圳鼎跃

近年来&#xff0c;随着水域活动增多及自然灾害频发&#xff0c;水下救援需求日益增长。传统人工打捞方法在复杂水域中效率低、风险高&#xff0c;尤其在能见度差、水流湍急或深水区域中&#xff0c;救援难度倍增。 在此背景下&#xff0c;水下声呐探测仪凭借其声波定位与视频…

AI 网关代理 LLMs 最佳实践

作者&#xff1a;付宇轩&#xff08;计缘&#xff09; DeepSeek/QWen 普惠 AI 趋势 随着 DeepSeek-R1 的横空出世&#xff0c;又一次点燃了原本已经有点冷淡的大语言模型市场和话题&#xff0c;并且快速成为了现象级&#xff0c;小到中小学生&#xff0c;大到父母辈都知道了中…

策略模式实际用处,改吧改吧直接用,两种方式

controller RestController RequestMapping("admin/test") RequiredArgsConstructor(onConstructor __(Autowired)) public class TestController {Autowiredprivate VideoFactory VideoFactory;GetMapping("getList")public R getList(){// 第一种方式T…

chromium魔改——修改 navigator.webdriver 检测

chromium源码官网 https://source.chromium.org/chromium/chromium/src 说下修改的chromium源码思路&#xff1a; 首先在修改源码过检测之前&#xff0c;我们要知道它是怎么检测的&#xff0c;找到他通过哪个JS的API来做的检测&#xff0c;只有知道了如何检测&#xff0c;我们…

Muduo网络库实现 [九] - EventLoopThread模块

目录 设计思路 类的设计 模块的实现 私有接口 公有接口 设计思路 我们说过一个EventLoop要绑定一个线程&#xff0c;未来该EventLoop所管理的所有的连接的操作都需要在这个EventLoop绑定的线程中进行&#xff0c;所以我们该如何实现将EventLoop和线程绑定呢&#xff1f;…

UE5学习笔记 FPS游戏制作38 继承标准UI

文章目录 UE的UIUMG的继承继承标准控件创建标准控件继承标准控件的用处 UE的UI 和Untiy有onGui和UGui类似&#xff0c;UE有slateUI和UMG,slateUI是早期只能用C编写的UI&#xff0c;UMG是现在使用的&#xff0c;可以拖拽编辑的UI slateUI是UMG的父类 UMG的继承 我们编写一个控…

C#核心学习(七)面向对象--封装(6)C#中的拓展方法与运算符重载: 让代码更“聪明”的魔法

目录 一、什么是拓展方法&#xff1f; 二、拓展方法有啥用&#xff1f;怎么写拓展方法&#xff1f; 1. ​核心用途 2. ​编写步骤 实现步骤 关键点说明 关键规则 3. ​注意事项 三、什么是运算符重载&#xff1f; 四、运算符重载有啥用&#xff1f;怎么写&#xff1f;…

银行卡归属地查询API接口如何对接?

银行卡归属地查询 API 接口是一种能让开发者通过编程方式获取银行卡归属地等相关信息的工具。借助此接口&#xff0c;开发者可将银行卡归属地查询功能集成到自己的应用程序或系统里&#xff0c;像电商平台、第三方支付公司等都能运用它来提升业务的准确性与安全性。 银行卡归属…

ORM mybits mybits-plus

ORM ORM 即对象关系映射&#xff08;Object Relational Mapping&#xff09;&#xff0c;是一种程序设计技术&#xff0c;用于实现面向对象编程语言里不同类型系统的数据之间的转换。下面从基本概念、工作原理、优势与劣势、常见的 ORM 框架等方面详细介绍 ORM。 常见的orm框架…

网络编程—网络概念

目录 1 网络分类 1.1 局域网 1.2 广域网 2 常见网络概念 2.1 交换机 2.2 路由器 2.3 集线器 2.4 IP地址 2.5 端口号 2.6 协议 3 网络协议模型 3.1 OSI七层模型 3.2 TCP/IP五层模型 3.3 每层中常见的协议和作用 3.3.1 应用层 3.3.2 传输层 3.3.3 网络层 3.3.4…

4月3日工作日志

一个朴实无华的目录 今日学习内容&#xff1a;1.关系数据库 今日学习内容&#xff1a; 1.关系数据库