Linux : 内核中的信号捕捉
目录
一 前言
二 信号捕捉的方法
1.sigaction()编辑
2. sigaction() 使用
三 可重入函数
四 volatile 关键字

一 前言
如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。在Linux: 进程信号初识-CSDN博客 这一篇中已经学习到了一种信号捕捉的调用接口:signal(),为了深入理解操作系统内核中的信号捕获机制,我们今天再来看一个接口:sigaction()。
二 信号捕捉的方法
1.sigaction()
🍪: sigaction 作为一个系统调用,用于修改和/或查询信号的处理方式。相较于 signal 函数来说,它提供了对信号处理函数更精细的控制。
- int signum:该参数需要传入指定的进程信号,表示要捕捉的信号。
- const struct sigaction *act:该参数与函数同名,是一个结构体指针。

- void (*sa_handler)(int) :此成员的含义其实就是自定义处理信号的函数指针;
- void (*sa_sigcation)(int, siginfo_t *, void *): 此成员也是一个函数指针. 但是这个函数的意义是用来 处理实时信号的, 本节不考虑分析。
- sigset_t sa_mask: 此成员是一个信号集。
- int sa_flags:此成员包含着系统提供的一些选项, 本篇文章使用中都设置为0
- void (*sa_restorer)(void):很明显 此成员也是一个函数指针. 但我们暂时不考虑他的意义.
也就是说我们暂时将该接口的第二个参数简单理解为一个结构体指针,并且结构体里有一个成员是用来自定义处理信号的。
- struct sigaction *oldact :第三个参数是输出型参数,且其作用是获取 此次修改信号 struct sigaction之前的原本的 struct sigaction , 如果传入的是空指针,则不获取。
2. sigaction() 使用
#include <iostream>
#include <signal.h>
#include <unistd.h>using namespace std;//自定义处理动作
void handler( int signo)
{cout<<"get a signno: " <<signo<<endl;
}
int main()
{struct sigaction act,oact;act.sa_handler=handler;//函数指针,自定义处理动作act.sa_flags=0;sigisemptyset(&act.sa_mask);//初始化信号集sa.masksigaction(SIGINT,&act,&oact);while(true) sleep(1);return 0;}
运行结果:

接下来我们对测试做一点修改
/其实就是相当于在handler中休眠了20s
//计时函数
void Count(int cnt)
{while(cnt){printf("cnt: %d\r",cnt);//\r回车fflush(stdout);//刷新缓冲区cnt--;sleep(1);}printf("\n");
}
//自定义处理动作
void handler( int signo)
{cout<<"get a signno: " <<"正在处理中"<<signo<<endl;Count(20);//休眠20s
}int main()
{struct sigaction act,oact;act.sa_handler=handler;//函数指针,自定义处理动作act.sa_flags=0;sigisemptyset(&act.sa_mask);//初始化信号集sa.masksigaction(SIGINT,&act,&oact);while(true) sleep(1);return 0;}
运行结果:

🍉:从运行结果我们可以看到,我们向进程发了大量2信号,但实际上进程只处理了几次(即20s处理一个信号)。当我们进行正在递达的某一个信号期间,同类信号无法递达,这是因为当前信号正在被捕捉时,系统会自动将当前信号加入到进程的信号屏蔽字。(block)
当信号完成捕捉动作,系统又会自动解除对该信号的信号屏蔽
当我们正在处理某一种信号的时候,我们也想屏蔽其他信号,我们可以利用 sigaction()函数中的一个参数 sa_mask,将额外想屏蔽的信号类型加入到sa_mask.
int main()
{struct sigaction act,oact;act.sa_handler=handler;//函数指针,自定义处理动作act.sa_flags=0;sigisemptyset(&act.sa_mask);//初始化信号集sa.mask
这是修改部分/sigaddset(&act.sa_mask,3);//除了屏蔽2号我们也想屏蔽3号sigaction(SIGINT,&act,&oact);while(true) sleep(1);return 0;}

小结:当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来 的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么 它会被阻塞到当前处理结束为止。 如果 在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则用sa_mask字段说明这些需 要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字。
🍎::signal() 和sigaction() 比较: 正如上文所说 相较于 signal 函数来说,它提供了对信号处理函数更精细的控制。一个直观的表现,在调用信号处理函数时,除了当前信号被自动屏蔽之外,还可以自动屏蔽另外一些信号。
三 可重入函数
可重入函数(Reentrant Function)是指可以在多线程环境中安全使用的函数,即这个函数可以被多个线程同时调用而不会导致数据错误或不一致。
下面用个例子解释一下:
一个进程中, 存在一个单链表结构. 并且此时需要执行一个节点的头插操作:

头插的操作就是:
node1->next = head;
head = node1;
如果在刚执行完第一步之后, 进程因为硬件中断或者其他原因 陷入内核了.
陷入内核之后需要回到用户态继续执行, 切换回用户态时 进程会检测未决信号, 如果此时刚好存在一个信号未决, 且此信号自定义处理.并且, 自定义处理函数动作也是一个新节点头插操作:

那么此时, 就会执行 node2 的头插操作, 执行完毕的结果就是:

即, node2 成为了链表的第一个节点 head,信号处理完毕之后, 需要返回用户继续执行代码, 用户刚执行完 node1->next = head; 所以下面应该执行 head = node1;, 结果就成了这样:

结果就是, node2 无法被找到了.造成了内存泄漏问题。
那么造成这个结果的原因是什么?
是因为单链表的头插函数, 被不同的控制流调用了, 并且是在第一次调用还没返回时就再次进入该函数, 这个行为称为 重入
像例子中这个单链表头插函数, 访问的是一个全局链表, 所以可能因为重入而造成数据错乱, 这样的函数被称为 不可重入函数, 即此函数不能重入, 重入可能会发生错误
反之, 如果一个函数即使重入, 也不会发生任何错误,这样的函数被称为可重入函数.
如果一个函数符合以下条件之一,则称为不可重入函数
调用了malloc和free。
调用了标准I/O库函数, 标准I/O库的很多实现都以不可重入的方式使用全局数据结构
四 volatile 关键字
在之前学习C/C++的时候,我们就已经接触过这个关键字了,它的作用是防止编译器对该关键字修饰的变量的优化,确保每次访问这个变量时都直接从内存中读取,而不是使用可能存在的寄存器中的缓存值。
接下来我们用一个例子分析一下该关键字
#include<stdio.h>#include<signal.h>int quit=0;void handler(int signo){printf(" %d 号信号:正在被捕捉\n",signo);printf("quit :%d",quit);quit=1;printf("->%d\n",quit); }int main(){signal(2,handler);//对2号信号进行捕捉while(!quit);//逻辑反,quit为0 !quit 为真 printf("我是正常退出的!\n");return 0;}

接下来我们做个改动

测试结果
我们看到虽然quit 变为1了,但是 我是正常退出的这句话并没有打印出来也就是说 while(!quit);
printf("我是正常退出的!\n"); 这个while循环依然在运行,这是为什么呢?
: 我们使用了 -03 选项让编译器做出了优化
🍪未优化前:

CPU访问数据时 会将数据从内存中拿到寄存器中. 然后再根据寄存器中的数据进行处理.所以在优化前,cpu一直在内存中读取quit值,main()和signal()是两个执行流,当2号信息传来的时候,signal()把quit的值改为1,main()执行流中cpu再次从内存读取quit的值时候已经变为1,跳出循环,所以打印了 我是正常退出的这句话。
🍪优化后:

所以 volatile 关键字的作用就是保持内存可见性
相关文章:
Linux : 内核中的信号捕捉
目录 一 前言 二 信号捕捉的方法 1.sigaction()编辑 2. sigaction() 使用 三 可重入函数 四 volatile 关键字 一 前言 如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。在Linux: 进程信号初识-CSDN博客 这一篇中已经学习到了一种信号…...
开发效率提升200%——cursor
cursor带来的编程"革命" 高级语言编程转为"自然语言编程"借助cursor,直接超越初级后台开发、超越初级前端开发、超越初级测试、超越初级UI,产研一体linux命令只用学不用记,语言描述就是命令给一个表结构流程提示词&…...
微软庆祝它成立整整50周年
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...
SpringBoot 整合 MCP
SpringBoot 整合 MCP MCP MCP 协议主要分为: Client 客户端(一般就是指 openai,deepseek 这些大模型)Server 服务端(也就是我们的业务系统)我们要做的就是把我们存量系统配置成 MCP Server 环境 JDK17…...
【详细】MySQL 8 安装解压即用 (包含MySQL 5 卸载)
卸载MySQL 1.卸载 2.安装目录删除残余文件(当初安装的位置) 3.删除programData下面的mysql数据文件 4.检查mysql服务是否存在,如果存在则删除(先暂停mysql服务) sc delete mysql 5.删除注册表中残留信息 安装MySQL 8&…...
显示器各类异常处理方法
显示器各类异常处理方法 导航 文章目录 显示器各类异常处理方法导航画面无显示/黑屏/无HDMI信号输入显示器闪烁显示器花屏显示画面模糊或扭曲显示器颜色异常显示器出现死点或亮点 画面无显示/黑屏/无HDMI信号输入 首先应该检查的是显示器电源(真的有人弄掉电源…...
关于Spring MVC中@RequestParam注解的详细说明,用于在前后端参数名称不一致时实现参数映射。包含代码示例和总结表格
以下是关于Spring MVC中RequestParam注解的详细说明,用于在前后端参数名称不一致时实现参数映射。包含代码示例和总结表格: 1. 核心作用 RequestParam用于显式绑定HTTP请求参数到方法参数,支持以下场景: 参数名不一致࿱…...
HTML的svg元素
<svg>元素 <svg>是一种用于描述二维矢量图形的 XML 格式,可以直接嵌入 HTML 文档中。 <svg>基本用法 <svg>的几种基本用法,包括圆形,正方形,三角形,直线 ,折线等 <body><svg widt…...
一、简单的 Django 服务
一、配置虚拟环境 1.1 创建一个文件夹在导航栏输入cmd打开 1.2 安装依赖两个库 pip install virtualenv virtualenvwrapper-win -i https://pypi.tuna.tsinghua.edu.cn/simple验证是否安装成功 virtualenv --version pip show virtualenvwrapper-win 1.3 创建虚拟环境 mkvi…...
二分算法的入门笔记
二分查找 使用前提:有序。可理解为枚举的一种技巧。时间复杂度: l o g ( n ) log(n) log(n) 基础模版代码 使用时根据情景进行相应的变化。注意跳出条件and分支处理方式and返回答案,三者之间的配合,不要进入死循环。可以在模拟…...
k8s黑科技:Linux+Vagrant+VirtualBox开启Kubernetes奇幻之旅
文章目录 1. 准备硬件2. 安装系统3. 安装 VNC4. 基础配置4.1 路由转发4.2 防火墙4.3 selinux4.4 安装包4.5 重启 5. 配置代理6. 安装 virtuabox7. 安装 vagrant8. 配置 kubespray8.1 安装依赖工具8.2 定制 Vagrantfile8.3 配置代理与时间同步8.4 配置私有镜像仓库 9. 安装虚拟机…...
34% 关税冲击下 LabVIEW 开发的变局
2025 年 4 月 4 日,中国国务院关税税则委员会宣布,自 4 月 10 日起对原产于美国的所有进口商品加征 34% 关税。这一举措,给 LabVIEW 开发领域带来显著影响,相关使用者和用户亟需采取应对策略。 从成本层面看,LabVI…...
42、JavaEE高级主题:WebSocket详解
WebSocket 一、WebSocket协议与实现 WebSocket是一种基于TCP协议的全双工通信协议,能够在客户端和服务器之间建立实时、双向的通信通道。通过WebSocket,客户端和服务器可以在任何时候发送数据,并立即接收到对方的响应。 1.1 WebSocket协议…...
Http代理服务器选型与搭建
代理服务器选型-Squid 缓存加速 缓存频繁访问的网页、图片等静态资源,减少对原始服务器的重复请求,提升响应速度支持HTTP、HTTPS、FTP等协议,通过本地缓存直接响应客户端请求 访问控制 基于ACL(访问控制列表)实现精细…...
蓝桥杯第十一届省赛C++B组真题解析
蓝桥杯第十一届省赛CB组真题解析 八、回文日期https://www.lanqiao.cn/problems/348/learning 方法一:暴力枚举所有的日期,记录有多少个回文日期。 #include <bits/stdc.h> using namespace std; int month[13]{0,31,28,31,30,31,30,31,31,30,31…...
Linux主要开发工具之gcc、gdb与make
此系列还有两篇,大家想完整掌握可以阅读另外两篇 Linux文本编辑与shell程序设计-CSDN博客 Linux基础知识详解与命令大全(超详细)-CSDN博客 1.gcc编译系统 1.1 文件名后缀 文件名后缀 文 件 类 型 文件名后缀 文 件 类 型 .c C源…...
排序算法(快速排序,选择排序......)【泪光2929】
hello,大家好!今天给大家分享一下各种排序: 1,选择排序 首先从原始数组中 选择最小的1个数据,将其和位于第1个位置的数据交换。接着从剩下的n-1个数据中选择次小的1个元素,将其和第2个位置的数据交换然后…...
Conda使用方法详解
Conda是一个开源的包管理和环境管理系统,主要用于Python/R等科学计算领域,可以轻松管理不同项目的依赖关系。以下是Conda的详细使用方法: 一、安装与配置 1.安装Miniconda/Anaconda Miniconda是精简版,只包含conda和Python Ana…...
数据库的MVCC机制详解
MVCC(Multi-Version Concurrency Control,多版本并发控制)是数据库系统中常用的并发控制机制,它允许数据库在同一时间点保存数据的多个版本,从而实现非阻塞的读操作,提高并发性能。 MVCC的核心思想是&…...
C++初阶-C++入门基础
目录 编辑 1.C的简介 1.1C的产生和发展 1.2C的参考文档 1.3C优势和难度 1.4C学习的建议 2.C的第一个程序 2.1打印Hello world 2.2头文件 2.3namespace命名空间 2.4::作用域限定符 2.5namespace的延伸 2.6C的输入输出 3.总结 1.C的简介 …...
关于量化交易在拉盘砸盘方面应用的部分思考
关于“砸盘”的深层解析与操盘逻辑 一、砸盘的本质与市场含义 砸盘指通过集中抛售大量筹码导致价格快速下跌的行为,其核心目标是制造恐慌、清洗浮筹或实现利益再分配。不同场景下的砸盘含义不同: 主动砸盘(操控…...
idea手动创建resources文件夹
有时maven没有构建成功可能造成,resources文件夹不创建的现象 此时我们可以手动创建 手动创建...
第十五届蓝桥杯大赛软件赛省赛Python 大学 C 组题目试做(中)【本期题目:回文数组,挖矿】
OK,继续写我们的第十五届蓝桥杯大赛软件赛省赛Python 大学 C 组题目,后面的题目比较麻烦了,所以我们再分两期讲。 这一期的题有 : 回文数组,挖矿 文章目录 回文数组基本思路第一步,获取半个数组每个数需要…...
Qt动画 QAbstractAnimation
文章目录 简介QVariantAnimation 数值动画QPropertyAnimation 属性动画 QAnimationGroup 一组动画QParallelAnimationGroup 并行动画组QSequentialAnimationGroup 串行动画组 简介 QAbstractAnimation 是所有 Qt 动画的基类。 该类定义了所有动画应该都会有的功能函数。 要想实…...
SpringMvc的请求-获得请求参数
客户端请求参数的格式是: namevalue&namevalue..… 服务器端要获得请求的参数,有时还需要进行数据的封装,SpringMVC可以接收如下类型的参数: 基本类型参数 POJO类型参数 数组类型参数 集合类型参数 获得基本类型参数 Controller中的业务方法…...
flutter开发音乐APP(前提准备)
1、项目的一些环境: 2、接口文档: 酷狗音乐 NodeJS 版 API 3、接口数据结构化 Instantly parse JSON in any language | quicktype UI样式借鉴参考: Coffee-Expert/Apple-Music-New-UI: Apple Music Clone on Flutter, with redesigned UI…...
使用docker搭建redis镜像时云服务器无法访问到国外的docker官网时如何解决
下载redis镜像 docker redis:版本号 此时截图中无法访问到国外的docker官网 解决方案: 通过更换镜像源来正常下载redis镜像 sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<EOF {"registry-mirrors": ["https://docker.1…...
双引擎驱动:解密音视频体验的QoS技术底座与QoE感官革命
QoS 定义:QoS(Quality of Service,服务质量)衡量音视频传输技术层面的性能表现,聚焦网络传输和系统处理能力,通过客观指标量化服务质量。核心指标 码率/带宽:数据传输速率上限,直接…...
Qt之QNetworkInterface
简介 用于表示网络接口(即网卡)信息 常用接口 static QList<QNetworkInterface> allInterfaces(); static QList<QHostAddress> allAddresses(); QList<QNetworkAddressEntry> addressEntries() const;接口类型 用枚举InterfaceTy…...
pom导包成功,但是就是无法使用相关类,同时报错:Library:Maven ‘xxx‘ has broken path
开发环境:Intellij 2023 一、问题记录 在maven工程的pom文件导入如下某一依赖(JGit)。没有显示导包的错误,同时在maven仓库里面找到对应的包是正常下载到相应jar的。 但是就是无法引入相关的类。打开Project Structure,在Dependencies中发现…...
