linux入门---信号的保存和捕捉
目录标题
- 信号的一些概念
- 信号的保存
- pending表
- block表
- handler表
- 信号的捕捉
- 内核态和用户态
- 信号的捕捉
信号的一些概念
1.进程会收到各种各样的信号,那么程序对该信号进行实际处理的动作叫做信号的递达。
2.我们之前说过当进程收到信号的时候可能并不会立即处理这个信号,而是先将信号进行保存,那么我们把信号从产生到抵达之间的状态称为信号未决。
3.进程可以阻塞某个信号,这样即使发送信号给对应的进程进程也不会做出任何的处理。
4.被阻塞的信号会一直保持在未决的状态,只有当进程解除了对此信号的阻塞时才会执行该信号的动作。
5.阻塞和忽略是两个不同的东西,一个信号要是被阻塞说明即使收到了该信号也不会执行该信号的动作,而忽略是收到该信号之后根据该信号执行的一个动作,只不过该动作是忽略也就是什么都不做而已。
信号的保存
之前的学习中我们知道操作系统是要给每个进程保存收到的信号的,而保存的位置就是task_struct中的一个位图,这是我们当时说的保存方法,但是信号存在三种状态比如说是否收到了信号,是否阻塞了信号,以及信号与之对应的动作以及动作是否被自定义修改了,所以内核使用一张图来保存信号肯定是不够用的,所以在实际的应用中使用了三张表一起来保存了信号,这三个表分别为:pending表,block表和handler表,那么接下来我们来一一介绍这三个表的功能。
pending表
pinding表本质上就是位图,我们之前说进程在运行的任何时候都会收到信号,这个信号并不会被立即处理,所以进程得保存这个信号,保存信号的方式是位图,那么这个位图就是pending表,位图的比特位位置表示哪个信号,比特位的值表示是否收到了该信号,如果收到了信号就将对应位置的值修改为1,如果没有收到信号对应位置的值就是0.

block表
block表也是一个位图,位图的位置表示信号编号,位图的值表示是否阻塞了对应的信号,被阻塞的信号不会被递达,只有当阻塞被解除了信号才会被递达,即使没有收到对应的信号我们也是可以阻塞一些信号的,这里的逻辑可以用下面的伪代码来表示:
if(1<<(signo)&pcb->block))
{//信号是被阻塞的,不递达
}
else
{if((1<<(signo-1))&pcb->pending){//递达该信号}
}
那这里就可以这么来理解,操作系统会扫描PCB中的位图来判断是否有信号需要被处理,如果信号对应在block上的值为1就直接跳过,如果对应在block上的值为0就接续查看对应在panding位图上的值,如果值为1的活就执行的对应的方法,那么这就是block表的功能。

handler表
handler表是一个函数指针数组,每个元素都是一个函数指针,数组的位置(下标)表示信号的编号,数组下标对应的内容就表示对应信号的处理方法,signal函数可以修改信号对应的方法,那么这个本质上就是修改handler表对应元素的指向,当信号被递达时就可以根据信号的值来找到handler表中处理信号的方法并执行该方法,那么这里的图片就是下面这样:

所以看到这里我们可以得出三个结论:如果一个信号没有产生的话,并不妨碍他可以先被阻塞,因为我们只需修改block表中的值跟记录是否收到信号的pending表没有关系,如果阻塞的过程中收到了信号,那么我们就修改pending位图上对应的值并不会接着去找handler表中的方法 2.进程为什么能够识别信号,是因为内核中存在三个表这三个表能让进程认识信号并处理对应的信号。 3.如果SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler。如果在进程解除对某信号的阻塞之前这种信号产生过多次,将如何处理?POSIX.1允许系统递送该信号一次或多次。Linux是这样实现的:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。
信号的捕捉
信号产生的时候不会立即被处理,而是在合适的时候被处理,那这个合适的时间是什么时候呢?并且我们上面说到操作系统会在某些时刻检查block表和pending表,那这个时刻又是指的什么时候呢?那么要想解决这些问题我们就得谈谈什么是内核太什么是用户态。
内核态和用户态
我们自己写的代码经过编译和运行之后都是运行在用户态上的,但是在我们在编写代码的时候难免会访问到操作系统的资源(getpid,waitpid)和硬件资源(printf,read,write),当我们访问操作系统的资源或者硬件资源的时候,我们都得直接或者间接的访问系统提供的借口,通过这些接口来访问操作系统的资源,我们把这些接口称为系统调用

调用系统调用的时候,普通用户不能以自己的身份来调用系统调用,必须将自己的身份变为内核态,
这就好比当我们是一个公司的基层时副总裁的办公室我们几乎不能直接进出,但是当我们的身份变成了公司的CEO时,副总裁的办公室我们就可以随意的进出,所以当我们的身份变成了内核态时我们才能够有资格使用或者访问内核态提供的代码,实际执行系统调用的人是进程,但是身份是内核态,因为用户态调用内核态的时候要发生身份的转换,所以系统调用往往比较费时间一些,所以要尽量避免频繁的调用系统调用

但是这里存在一个问题,你说进程只是那一个进程,但是却存在两个身份,那该操作系统是如何来实现这一点的呢?那么这里我们就得提到寄存器这个东西
cpu中存在大量的寄存器,寄存器分为两种:1.可见寄存器,2.不可见寄存器,虽然寄存器分为两种但是只要和当前进程强相关的寄存器我们都将其称为上下文数据,比如说cpu中存在一个寄存器专门用来指向当前进程的pcb这样就可以快速的知道当前运行的进程是哪个,再比如说有个寄存器中存储的是当前页表的起始地址,这样就可以快速的找到当前进程的页表,
cpu中还存在一个名为CR3的寄存器,这个寄存器中的内容表征当前进程的运行级别,0表示内核态,3表示用户态,所以想要判断当前的进程是用户态还是内核态就只需要查看CR3里面的内容就行,这里存在一个问题:我是一个进程,进程是怎么跑到内核中执行对应的方法呢?之前我们提到的进程地址空间往往指的是用户空间,我们写的代码存放或者申请空间都是通过用户级地址空间+页表来进行映射,这个空间的大小为3GB

这里的页表是用户级页表该页表每个进程都有一份,但是地址空间的总大小是4GB,用户级空间只占了3GB那剩下的1GB空间用来干什么呢?我们把这个1GB的空间称为内核级空间,这一块空间也对应了一个页表,这个页表就叫内核级页表,这个页表的作用就是将操作系统级别的代码从内核虚拟地址空间映射到内存上面,

因为操作系统的代码只有一份,在机器启动的时候会将这份代码加载到内存上,并且每个进程都要使用操作系统代码,所以内核级页表只有一份,每个进程都通过这个页表来映射找到内存上的操作系统的代码,每一个进程都有自己的地址空间(用户空间独占),内核空间(被映射到了每个进程的地址空间的3-4G),所以进程要想访问操作系统的接口,其实只需要在自己的地址空间上进行跳转即可,由用户区跳转到内核区然后再由用户级页表跳转访问内存上的内核代码即可,每一个进程的进程地址空间都有3-4GB的内容,都会共享一个内核级页表,所以无论进程如何切换都不会修改进程地址空间中3-4GB的内容。那用户凭什么能够执行并访问内核的接口或者数据呢?原因是当操作系统发现你要访问内核的数据和代码时,会帮进程修改CR3寄存器的内容,这样就从用户态变成了内核态那么这时就有权利访问内核的数据,那操作系统为什么能知道我们要访问内核的数据和代码呢?答案是当我们要访问操作系统的接口时最开始还是用户态执行的,当以用户态执行系统调用接口时,系统调用接口做的最多的事情还是讲寄存器中的3号状态修改成为0号状态也就是将用户态改成内核态,然后才跳转区域访问内核的数据和代码,那么这就是用户态和内核态的概念
信号的捕捉
我们说操作系统会在合适的时候处理信号,那么这个合适的时候就是从内核态返回用户态的时候对信号进行处理,那么这就说明我们之前一定先进入了内核态,

当执行系统调用和进程切换(一个进程没有执行完,那一定是放到运行队列或者等待队列,那放进队列的时候一定是以操作系统的身份把我放了进去,所以得先变成内核态 就会变成内核态)

当操作系统进入内核态并且马上要返回普通状态的时候还会做一件事就是检查内核中的三张表,

如果block中的信号为0,pending中的信号也为0就会接着检查下一个信号,当block中的值为1就算pending中的值为1的话也不会执行该信号,当block的值为0并且penging的值为1的话就会执行该信号,然后就去handler中查找对应的方法,处理的方法有默认,忽略,自定义,默认的方法中有70%是终止进程,因为当前的身份是内核态所以可以很轻松的终止当前进程,忽略就是要处理该信号,但是处理的动作是什么都不做,所以直接将pending对应位置的信号设置为0就行,自定义的方法的实现在用户态,那这里就存在一个问题:我们能不能以内核态的身份来执行用户的代码呢?从技术的角度上我们可以以内核态的身份访问用户的代码,但是从设计的角度上来看是不能的,因为操作系统不相信任何人,handler方法中可能会有对系统造成危险的操作,而这些操作操作系统是无法识别是安全还是有害的,所以当我们以内核态的身份执行用户的代码的话,这部分代码可能会被恶意分子利用,然后进行一部分越权的非法动作,所以即使从技术上的角度能这么干,操作系统也不让你这么干,所以当要执行自定义代码的时候操作系统得通过特定的调用,将自己的身份从内核态转换称为用户态,然后再执行自定义代码,那么这个时候自定方法出现了问题就是用户自己的问题了,这个时候想干什么坏事情就会收到操作系统的管制了

执行完自定义方法之后也不能直接返回之前调用方法的地方,因为我们无法知道当初是从哪里进行跳转的,不能从用户态的一个地方跳转到另外一个地方,这里必须得有操作系统的支持,因为在从用户态跳转到内核态的时候操作系统保存了你的上下文信息,所以执行完自定义方法后得从用户态再跳转回内核,再通过特定的系统调用由内核态回到用户态上次的地方再继续向后执行

那么这就是信号捕捉的全部流程希望大家能够理解。
相关文章:
linux入门---信号的保存和捕捉
目录标题 信号的一些概念信号的保存pending表block表handler表 信号的捕捉内核态和用户态信号的捕捉 信号的一些概念 1.进程会收到各种各样的信号,那么程序对该信号进行实际处理的动作叫做信号的递达。 2.我们之前说过当进程收到信号的时候可能并不会立即处理这个信…...
5.外部中断
中断初始化配置步骤: IO口初始化配置 开启中断总允许EA 打开某个IO口的中断允许 打开IO口的某一位的中断允许 配置该位的中断触发方式 中断函数: #pragma vector PxINT_VECTOR __interrupt void 函数名(void){}#pragma vector PxINT_VECTOR __int…...
Mydb数据库问题
1、请简要介绍一下这个基于 Java 的简易数据库管理系统。它的主要功能是什么? TM(Transaction Manager):事务管理器,用于维护事务的状态,并提供接口供其他模块查询某个事务的状态。DM(Data Man…...
部署并应用ByteTrack实现目标跟踪
尽管YOLOv8已经集成了ByteTrack算法,但在这里我还是想利用ByteTrack官网的代码,自己实现目标跟踪。 要想应用ByteTrack算法,首先就要从ByteTrack官网上下载并安装。虽然官网上介绍得很简单,只需要区区6行代码,但对于国…...
MacOS怎么配置JDK环境变量
1 输入命令看是否配置了JDk 的环境变量:echo $JAVA_HOME 要是什么也没输出 证明是没配置 2 输入命令编辑 sudo vim ~/.bash_profile 然后按 i ,进入编辑模式,粘贴下面的代码,注意:JAVA_HOME后面路径需要改成自己的版…...
Spring Boot 开发16个实用的技巧
当涉及到使用Spring Boot开发应用程序时,以下是16个实用的技巧: 1. **使用Spring Initializr**:Spring Initializr是一个快速创建Spring Boot项目的工具,可以帮助您选择项目依赖和生成项目骨架。 2. **自动配置**:Sp…...
《机器学习实战》学习记录-ch2
PS: 个人笔记,建议不看 原书资料:https://github.com/ageron/handson-ml2 2.1数据获取 import pandas as pd data pd.read_csv(r"C:\Users\cyan\Desktop\AI\ML\handson-ml2\datasets\housing\housing.csv")data.head() data.info()<clas…...
lv7 嵌入式开发-网络编程开发 07 TCP服务器实现
目录 1 函数介绍 1.1 socket函数 与 通信域 1.2 bind函数 与 通信结构体 1.3 listen函数 与 accept函数 2 TCP服务端代码实现 3 TCP客户端代码实现 4 代码优化 5 练习 1 函数介绍 其中read、write、close在IO中已经介绍过,只需了解socket、bind、listen、acc…...
mysql技术文档--阿里巴巴java准则《Mysql数据库建表规约》--结合阿丹理解尝试解读--国庆开卷
阿丹: 国庆快乐呀大家! 在项目开始前一个好的设计、一个健康的表关系,不仅会让开发变的有趣舒服,也会在后期的维护和升级迭代中让系统不断的成长。那么今天就认识和解读一下阿里的准则!! 建表规约 表达是…...
Qt+openCV学习笔记(十六)Qt6.6.0rc+openCV4.8.1+emsdk3.1.37编译静态库
前言: 有段时间没来写文章了,趁编译库的空闲,再写一篇记录文档 WebAssembly的发展逐渐成熟,即便不了解相关技术,web前端也在不经意中使用了相关技术的库,本篇文档记录下如何编译WebAssembly版本的openCV&…...
JUC第十四讲:JUC锁: ReentrantReadWriteLock详解
JUC第十四讲:JUC锁: ReentrantReadWriteLock详解 本文是JUC第十四讲:JUC锁 - ReentrantReadWriteLock详解。ReentrantReadWriteLock表示可重入读写锁,ReentrantReadWriteLock中包含了两种锁,读锁ReadLock和写锁WriteLockÿ…...
在vue3中使用vite-svg-loader插件
vite-svg-loader插件可以让我们像使用vue组件那样使用svg图,使用起来超级方便。 安装 npm install vite-svg-loader --save-dev使用 import svgLoader from vite-svg-loaderexport default defineConfig({plugins: [vue(), svgLoader()] })组件里使用 在路径后加…...
国庆10.4
QT实现TCP服务器客户端 服务器 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> //服务器头文件 #include <QTcpSocket> //客户端头文件 #include <QList> //链表容器 #include <QMe…...
2023/8/12 下午8:41:46 树状控件guilite
2023/8/12 下午8:41:46 树状控件guilite 2023/8/12 下午8:42:08 树状控件(Tree View)是一种常见的图形用户界面(GUI)元素,它通常用于显示层次结构数据或文件系统的目录结构。Guilite 是一个轻量级的跨平台 GUI 库,支持多种控件,包括树状控件。 在 Guilite 中使用树状…...
BL808学习日志-2-LVGL for M0 and D0
一、lvgl测试环境 对拿到的M1S_DOCK开发板进行开发板测试,博流的官方SDK是支持M0和D0两个内核都进行测试的;但是目前只实现了M0的LVGLBenchmark,测试D0内核中发现很多莫名其妙的问题。一会详细记录。 使用的是开发板自带的SPI显示屏ÿ…...
treectrl类封装 2023/8/13 下午4:07:35
2023/8/13 下午4:07:35 treectrl类封装 2023/8/13 下午4:07:53 TreeCtrl 类是一个常用的图形用户界面控件,用于实现树形结构的展示和交互。以下是一个简单的 TreeCtrl 类的封装示例: python import wxclass MyTreeCtrl(wx.TreeCtrl):def __init__(self, parent):super()…...
Android学习之路(20) 进程间通信
IPC IPC为 (Inter-Process Communication) 缩写,称为进程间通信或跨进程通信,指两个进程间进行数据交换的过程。安卓中主要采用 Binder 进行进程间通信,当然也支持其他 IPC 方式,如:管道,Socket࿰…...
机器学习——KNN算法流程详解(以iris为例)
、 目 录 前情说明 问题陈述 数据说明 KNN算法流程概述 代码实现 运行结果 基于可视化的改进 可视化代码 全部数据可视化总览 分类投票结果 改进后最终代码 前情说明 本书基于《特征工程入门与入门与实践》庄家盛 译版P53页K最近邻(KNN)算…...
国庆假期day5
作业:请写出七层模型及每一层的功能,请绘制三次握手四次挥手的流程图 1.OSI七层模型: 应用层--------提供函 表示层--------表密缩 会话层--------会话 传输层--------进程的接收和发送 网络层--------寻主机 数据链路层----相邻节点的可靠传…...
ES6中的let、const
let ES6中新增了let命令,用来声明变量,和var类似但是也有一定的区别 1. 块级作用域 只能在当前作用域内使用,各个作用域不能互相使用,否则会报错。 {let a 1;var b 1; } console.log(a); // 会报错 console.log(b); // 1为什…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用
在工业制造领域,无损检测(NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统,以非接触式光学麦克风技术为核心,打破传统检测瓶颈,为半导体、航空航天、汽车制造等行业提供了高灵敏…...
大数据治理的常见方式
大数据治理的常见方式 大数据治理是确保数据质量、安全性和可用性的系统性方法,以下是几种常见的治理方式: 1. 数据质量管理 核心方法: 数据校验:建立数据校验规则(格式、范围、一致性等)数据清洗&…...
RKNN开发环境搭建2-RKNN Model Zoo 环境搭建
目录 1.简介2.环境搭建2.1 启动 docker 环境2.2 安装依赖工具2.3 下载 RKNN Model Zoo2.4 RKNN模型转化2.5编译C++1.简介 RKNN Model Zoo基于 RKNPU SDK 工具链开发, 提供了目前主流算法的部署例程. 例程包含导出RKNN模型, 使用 Python API, CAPI 推理 RKNN 模型的流程. 本…...
多模态学习路线(2)——DL基础系列
目录 前言 一、归一化 1. Layer Normalization (LN) 2. Batch Normalization (BN) 3. Instance Normalization (IN) 4. Group Normalization (GN) 5. Root Mean Square Normalization(RMSNorm) 二、激活函数 1. Sigmoid激活函数(二分类&…...
【汇编逆向系列】四、函数调用包含单个参数之Double类型-mmword,movsd,mulsd,addsd指令,总结汇编的数据类型
一、汇编代码 上一节开始,讲到了很多debug编译独有的汇编方式,为了更好的区分release的编译器优化和debug的区别,从本章节开始将会提供debug和release的汇编用作对比 Debugb编译 single_double_param:00000000000000A0: F2 0F 11 44 24 08…...
Polarctf2025夏季赛 web java ez_check
第一次自己做出一个java,值得小小的记录,polar的java真得非常友好 反编译jar包,一眼就看到有个/deserialize 路由,接受base64的序列化数据,base64解码后 经过一次kmp检查,再由SafeObjectInputStream来反序列…...
springboot启动mapper找不到方法对应的xml
数据源配置 目录结构 idea中mapper.java 可以找到对应的mapper.xml文件 启动却找不到 因为mapper.db1会被识别为文件名 而非目录结构 调整为这种...
