Linux 信号捕捉与处理
💓博主CSDN主页:麻辣韭菜💓
⏩专栏分类:Linux知识分享⏪
🚚代码仓库:Linux代码练习🚚
🌹关注我🫵带你学习更多Linux知识
🔝
目录
前言
1. 信号的处理时机
1.1用户态与内核态
1.2再谈地址空间
1.3 信号处理过程
1. 4 内核如何实现信号的捕捉
2. sigaction
功能
参数
结构体定义
注意事项
信号处理函数
代码演示
3. 额外知识
3.1可重入函数
3.2 volatile
3.3 SIGCHLD 信号
前言
信号产生到处理这是有一个过程的,从上篇信号保存我们知道了有一个handler表 里面存放的是对信号处理的方法。
那什么时候调用这些方法?本篇就揭晓处理信号的时机。
1. 信号的处理时机
直接说结论:当我们用户从内核态返回到用户态时,进行信号的检测与处理。
那什么是用户态?什么又是内核态?
1.1用户态与内核态
用户态:我们写的代码被执行时,就是用户态。
内核态:执行系统代码时,就是内核态。
如何理解内核态?
我们之前所学的fork创建子进程。 open read witre close这些系统调用接口被执行时,进程就从用户态切换成了内核态。
在调用这些系统调用接口。其实有都有一条汇编指令:int 80 (用户态切换成内核态)
也就是说执行我们代码时,是会产生中断或者异常,当CPU在处理中断或异常时,会根据中断描述符表(Interrupt Descriptor Table, IDT)中的描述符的DPL来决定是否需要进行上下文切换。如果当前的Code Segment
的DPL高于或等于IDT中描述符的DPL,那么CPU将不会切换到一个新的特权级别,否则会进行上下文切换。
回想之前的进程地址空间图, 高地址的 3G 到 4G是内核空间,所以我们需要从新再谈地址空间,才能更好的理解用户状态的切换。
1.2再谈地址空间
每个进程都会有自己的地址空间和用户级页表,因为进程具有独立性。
从上图可以看到每个地址空间的3G到4G是内核空间。 也就是说执行操作系统代码以及系统调用接口的代码和数据是在这一个区域。
那是不是每个进程都要有内核级的页表?
当然不用。内核级页表只有一份!!!
每一个进程 在0到3GB 所看到的代码和数据是不一样的。内核级页表只有一份,就意味着3GB到4GB这段空间,所有进程看到的都是同样的代码和数据(OS)所以 不管怎么切换进程 这段空间的内容是不变的。
那进程又是如何被调度的?
不要忘了OS也是软件啊。并且还是一个死循环等待指令软件。
当然等待指令也是需要硬件的。时钟硬件 每格一段时间就会向cpu发送信息。OS这时就会知道,那个进程的时间片到了 然后通过一系列系统调用函数
switch_to()
宏,以及底层的context_switch()
函数。进行上下文切换。
1.3 信号处理过程
这里需要解释用户自定义为什么要回到用户模式执行处理方法。
有人就会说 在第2步时,已经在内核态了 可以执行信号处理的方法。为什么还要回到用户态?
因为OS对除了自己的系统调用对任何的代码都不相信,所以OS检测到是用户定义方法时。会切换状态。变成用户态,用户态执行信号处理。再次切换成内核态
那为什么还要返回到内核态?
因为进程的上下文还在内核态中,自定义函数栈帧和进程的栈帧不是同一个。拿什么返回上下文的数据?
上面的图有点复杂 我们用简化的版图 数学上的无穷符号
1. 4 内核如何实现信号的捕捉
2. sigaction
#include <signal.h>
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
功能
sigaction()
系统调用用于改变进程接收特定信号时采取的动作。
参数
signum
: 指定信号的标识符,可以是任何有效的信号,除了SIGKILL
和SIGSTOP
。act
: 如果非空,表示要安装的新信号动作,来源于act
。oldact
: 如果非空,表示先前的信号动作将被保存在oldact
中。
结构体定义
struct sigaction {void (*sa_handler)(int); // 传统信号处理函数void (*sa_sigaction)(int, siginfo_t *, void *); // SA_SIGINFO 标志设置时使用的信号处理函数sigset_t sa_mask; // 信号掩码,定义了信号处理函数执行时哪些信号应当被屏蔽int sa_flags; // 信号处理标志void (*sa_restorer)(void); // 已废弃,不应使用
};
注意事项
- 结构体中
sa_handler
和sa_sigaction
不应同时被赋值,它们是互斥的。 sa_restorer
成员已废弃,POSIX 标准中没有指定该成员。sa_flags
中设置SA_SIGINFO
标志时,使用sa_sigaction
成员,否则使用sa_handler
。
信号处理函数
sa_handler
: 可以是SIG_DFL
(默认动作)、SIG_IGN
(忽略信号)或者指向信号处理函数的指针。处理函数仅接收信号编号作为参数。- 如果
sa_flags
包含SA_SIGINFO
,则sa_sigaction
指定信号处理函数,该函数接收三个参数:信号编号、指向siginfo_t
的指针(包含信号的详细信息),以及指向ucontext_t
的指针(通常被转换为void *
类型,包含了信号发生时的上下文信息)。
代码演示
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <signal.h>
using namespace std;
void PrintPending()
{sigset_t set;sigpending(&set);for (int signo = 31; signo >= 1; signo--){if (sigismember(&set, signo)){cout << "1";}elsecout << "0";}cout << endl;
}void handler(int signo)
{cout << " catch a signal ,signal number: " << signo << endl;while (true){PrintPending();sleep(1);}
}int main()
{// 1.使用sigaction函数struct sigaction act, oact;// 1.1初始化 act oact。memset(&act, 0, sizeof(act));memset(&oact, 0, sizeof(oact));// 2.1 初始化 并设置信号屏蔽集sigemptyset(&act.sa_mask);sigaddset(&act.sa_mask, 1);sigaddset(&act.sa_mask, 3);sigaddset(&act.sa_mask, 4);// 3.自定义handler函数act.sa_handler = handler;//3.1 给2号信号注册自定义动作。sigaction(2, &act, &oact);while (true){cout << "I am a process: " << getpid() << endl;sleep(1);}// 4. 发送信号return 0;
}
可以从运行结果来看,我们发送4号信号确实被阻塞了。但是未决表中有信信息。
3. 额外知识
掌握额外知识在后面线程章节中,能更好的理解线程。
3.1可重入函数
有没有一种可能 两个进程同时执行容器 list 的插入函数 insert, 当进程1开辟好空间时,刚好进程1的调度时间片到了切换成进程2。而进程2调度时间,刚好可以把insert这个函数执行完毕。然后进程1再次被调度执行insert。这时进程2的insert函数调用new开辟的空间就会出现内存泄漏。
当然大家也不用担心。
我们学过的99%的函数都是不可被重入。
那有没有需要重入的场景?
操作系统服务:操作系统提供的某些服务,如内存分配器,必须是可重入的,因为它们可能会被不同的进程或线程同时调用。
那什么样的函数可以重入?
不使用全局变量或静态变量:可重入函数不依赖于全局或静态变量来存储状态,因为这些变量可能被多个线程共享,从而导致数据竞争。
不执行I/O操作:I/O操作可能会涉及系统资源,如文件句柄,这些资源在多线程环境中可能会引起冲突。
不调用其他不可重入的函数:如果可重入函数调用了不可重入的函数,它自身也就不再是可重入的了。
不返回指向内部数据结构的指针:函数返回的指针不应该指向函数内部的局部变量或分配的内存,因为这些内存在函数返回后可能会被释放。
不产生副作用:可重入函数不应该改变程序的其他部分的状态,除了其返回值。
使用局部变量:所有需要的变量都应该在函数内部分配,通常是在栈上。
使用互斥锁或原子操作:如果必须访问共享资源,可重入函数应该使用互斥锁(mutexes)或原子操作来确保线程安全。
避免使用动态内存分配:虽然技术上可行,但动态内存分配(如使用
malloc
或new
)可能会使函数不可重入,因为这些操作可能会依赖于全局或静态状态。快速执行:可重入函数应该设计成执行时间尽可能短,以减少它被中断的可能性。
信号安全:如果函数设计用于响应信号(如在UNIX系统中),它应该是信号安全的,即它可以在信号处理的上下文中安全调用。
3.2 volatile
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <signal.h>
using namespace std;
int n = 0;
void handler(int signo)
{cout << " catch a signal ,signal number: " << signo << endl;n = 1;
}int main()
{signal(2, handler);while (!n);cout << "process quit normal" << endl;return 0;
}
初步结果符合预期,2
号信号发出后,循环结束,程序正常退出
那是因为当前编译器默认的优化级别很低,没有出现意外情况
通过指令查询 g++
优化级别的相关信息
man g++
: /O1
其中数字越大,优化级别越高,理论上编译出来的程序性能会更好
那我们再改成 -O1试试
g++ -o $@ $^ -g -O1 -std=c++11
编译成功 运行得到下面的结果。
此时得到了不一样的结果:
2
号信号发出后,对于 falg
变量的修改似乎失效了
从上面的实验的,我们知道编译肯定是优化了,不然为什么while循环一直不退出。
为什么优化了,就会出现 n 的值修改失效?
为了防止编译器过度优化,votatile 这个关键字就出现了。它的作用就是防止编译过度优化。
volatile int n = 0;
这时我们在编译
3.3 SIGCHLD 信号
在进程控制学习期间, 我们知道父进程必须等待子进程的退出然后回收。以前没有信号的概念,那子进程退出后,会给父进程发信号吗?
当然会发信号 子进程退出后会给父进程发送 SIGCHLD 信号
SIGCHLD 是普通信号的17号位
下面我们用一段代码证明
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
using namespace std;void handler(int signo)
{cout << " catch a signal ,signal number: " << signo << endl;}int main()
{signal(17,handler);for(int i = 0 ; i<10 ; i++){pid_t pid = fork();if(pid == 0){while(true){cout << "I am a child process: " << getpid() << " ppid: " << getppid() << endl;sleep(1);break;}cout << " child quit !!! " << endl;exit(0);}sleep(1);}while (true){cout << "I am father process: " << getpid() << endl;sleep(1);}return 0;
}
从面结果来看确实回收了子进程。但是这个代码有问题。不要忘了pending表的位图17只有一位。如果回收多个进程那么OS只会做一次信号处理,而其他的子进程没有回收就造成内存泄漏。那如何解决?
自定义捕捉函数中,采取
while
循环式回收,有很多进程都需要回收没问题,排好队一个个来就好了,这样就可以确保多个子进程同时发出SIGCHLD
信号时,可以做到一一回收
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
using namespace std;void handler(int signo)
{pid_t rid;while ((rid = waitpid(-1, nullptr, WNOHANG)) > 0){cout << "I am proccess: " << getpid() << " catch a signo: " << signo << "child process quit: " << rid << endl;}}int main()
{signal(17,SIG_IGN);for(int i = 0 ; i<10 ; i++){pid_t pid = fork();if(pid == 0){while(true){cout << "I am a child process: " << getpid() << " ppid: " << getppid() << endl;sleep(1);break;}cout << " child quit !!! " << endl;exit(0);}sleep(1);}while (true){cout << "I am father process: " << getpid() << endl;sleep(1);}return 0;
}
这里说一说为什么用 signal 函数参数为什么是 SIG_IGN
这里的忽略是个特例,只是父进程不对其进行处理,但只要设置之后,子进程在退出时,由 操作系统 对其负责,自动清理资源并进行回收,不会产生 僵尸进程
原理:
父进程的 PCB
中有关僵尸进程处理的标记位会被修改,子进程继承父进程的特性,子进程在退出时,操作系统检测到此标记位发生了改变,会直接把该子进程进行释放。
注意: SIG_IGN 只在linux中有效,其他系统无效比如UNIX
相关文章:

Linux 信号捕捉与处理
💓博主CSDN主页:麻辣韭菜💓 ⏩专栏分类:Linux知识分享⏪ 🚚代码仓库:Linux代码练习🚚 🌹关注我🫵带你学习更多Linux知识 🔝 目录 前言 1. 信号的处理时机 1.1用户…...

桂林电子科技大学计算机工程学院、广西北部湾大学计信学院莅临泰迪智能科技参观交流
5月18日,桂林电子科技大学计算机工程学院副院长刘利民、副书记杨美娜、毕业班辅导员黄秀娟、广西北部湾大学计信学院院长助理刘秀平莅临广东泰迪智能科技股份有限公司产教融合实训基地参观交流。泰迪智能科技副总经理施兴、广西分公司郑廷和、梁霜、培训业务部孙学镂…...

Qt笔记:动态处理多个按钮点击事件以更新UI
问题描述 在开发Qt应用程序时,经常需要处理多个按钮的点击事件,并根据点击的按钮来更新用户界面(UI),如下图。例如,你可能有一个包含多个按钮的界面,每个按钮都与一个文本框和一个复选框相关联…...

Excel模板计算得出表格看板
背景 表格看板及导出,单元格时间年是根据筛选器时间变化的 较往年和往年是计算单元格 思路 1.通过excel模板来把数据填入excel再数据清洗得到数据返回前端 2.数据填充,通过行列作为key 列如:key整体20241月,根据key匹配数据填…...

es数据备份和迁移Elasticsearch
Elasticsearch数据备份与恢复 前提 # 注意: 1.在进行本地备份时使用--type需要备份索引和数据(mapping,data) 2.在将数据备份到另外一台ES节点时需要比本地备份多备份一种数据类型(analyzer,mapping,data,template) …...
Oracle数据块之数据行中的SCN
从Oracle 10g开始,如果在表级别打开ROW DEPENDENCIES,业务数据行发生更改时会在数据块中进行登记。 可以通过DUMP数据块来观察上述SCN: (1)创建测试表,插入3条测试数据,插入一条提交一次。并调用…...

手写tomcat(Ⅱ)——Socket通信+tomcat静态资源的获取
Socket通信简介 参考文章:socket通讯原理及例程(一看就懂) socket是介于应用层(http协议)和传输层(TCP/UDP协议)之间的一层虚拟层 Socket是一个程序,符合TCP/UDP协议的规范&…...

解决Error: error:0308010C:digital envelope routines::unsupported的四种解决方案
问题描述: 报错:Error: error:0308010C:digital envelope routines::unsupported 报错原因: 主要是因为 nodeJs V17 版本发布了 OpenSSL3.0 对算法和秘钥大小增加了更为严格的限制,nodeJs v17 之前版本没影响&am…...

shell 脚本笔记2
3.env与set区别 env用于查看系统环境变量 set用于查看系统环境变量自定义变量函数 4.常用环境变量 变量名称含义PATH命令搜索的目录路径, 与windows的环境变量PATH功能一样LANG查询系统的字符集HISTFILE查询当前用户执行命令的历史列表 Shell变量:自定义变量 目标…...

aws eks集成wasm运行时并启动pod
参考资料 WebAssembly 在云原生中的实践指南,https://cloud.tencent.com/developer/article/2324065 作为一种通用字节码技术,wasm的初衷是在浏览器中的程序实现原生应用性能。高级语言将wasm作为目标语言进行编译并运行在wasm解释器中。和nodejs类似的…...
linux:切分大文件
文章目录 1. 前言2. 用法3. 例子 1. 前言 如果传输、存储过程中出现大文件,希望切分成小文件。在 Linux 中,可以使用多种工具来切分大文件,最常用的是 split 命令。split 命令可以将一个大文件按照指定大小切分成多个小文件。 2. 用法 spl…...
docker 配置文件使用经验,后续持续增加
1. 容器中如何访问主机服务 在docker容器、docker compose 中如何访问主机服务呢? docker容器 20.10.0 版本在 linux 新增 host.docker.internal 支持: docker run -it --add-hosthost.docker.internal:host-gateway alpine cat /etc/hosts 127.0.0.…...
Qml:键盘事件
import QtQuickWindow {width: 640height: 480visible: truetitle: qsTr("Test KeyEvent")//传递给活动窗口的QQuickWindow//传递给当前活动的Item(focus为true),如没则找子节点中的,都没有则忽略Item{id:item1//focus:…...
Java列表导出时将附件信息压缩成一个zip
一:使用场景 在最近的工作当中遇到了一个需求,在列表导出时,不仅需要将列表信息导出为excel文件,同时也需要将列表每一条数据所对应的附件信息放在同一个文件夹当中,并且压缩成一个zip响应给浏览器。首先后端需要写两…...

简单美观易上手的 Docker Compose 可视化管理器 Dockge
本文首发于只抄博客,欢迎点击原文链接了解更多内容。 前言 Dockge 是 Uptime Kuma 作者的新作品,因此 UI 风格与 Uptime Kuma 基本一致,如果你正在使用 Uptime Kuma 的话,那么 Dockge 的 UI 设计应该也不会让你失望。Dockge 主打…...

贴片 RS8752XK 封装SOP-8 250MHz,2通道高速运放
传感器信号放大:在传感器应用中,RS8752XK可以用于放大微弱的传感信号,如压力、温度、光强等传感器的信号。 数据采集系统:在数据采集设备中,RS8752XK可以用于放大和调理模拟信号,以供模数转换器࿰…...
图论-最短路算法
1. Floyd算法 作用:用于求解多源最短路,可以求解出任意两点的最短路 利用动态规划只需三重循环即可(动态规划可以把问题求解分为多个阶段)定义dp[k][i][j]表示点i到点j的路径(除去起点终点)中最大编号不超…...

家政预约小程序05服务管理
目录 1 设计数据源2 后台管理3 后端API4 调用API总结 家政预约小程序的核心是展示家政公司提供的各项服务的能力,比如房屋维护修缮,家电维修,育婴,日常保洁等。用户在选择家政服务的时候,价格,评价是影响用…...

Django自定义命令
Django自定义命令 我们知道,Django内部内置了很多命令,例如 python manage.py runserver python manage.py makemigrations python manage.py migrate我们可以在python控制台中查看所有命令 我们也可以自定义命令,让python manage.py执行…...
详解VLSM技术
在现代网络设计中,如何高效地分配和管理IP地址是一个关键问题。传统的子网划分方法虽然简单,但在实际应用中常常导致IP地址的浪费。为了应对这一问题,VLSM(Variable Length Subnet Mask,可变长子网掩码)技术…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...

Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...

【Linux】自动化构建-Make/Makefile
前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具:make/makfile 1.背景 在一个工程中源文件不计其数,其按类型、功能、模块分别放在若干个目录中,mak…...

9-Oracle 23 ai Vector Search 特性 知识准备
很多小伙伴是不是参加了 免费认证课程(限时至2025/5/15) Oracle AI Vector Search 1Z0-184-25考试,都顺利拿到certified了没。 各行各业的AI 大模型的到来,传统的数据库中的SQL还能不能打,结构化和非结构的话数据如何和…...
计算机系统结构复习-名词解释2
1.定向:在某条指令产生计算结果之前,其他指令并不真正立即需要该计算结果,如果能够将该计算结果从其产生的地方直接送到其他指令中需要它的地方,那么就可以避免停顿。 2.多级存储层次:由若干个采用不同实现技术的存储…...