Linux 信号终篇(总结)
前文:本文是对信号从产生到被处理的过程中的概念和原理的总结,如果想了解具体实现,请查看前两篇博客:Linux 信号-CSDN博客、Linux 信号(下篇)-CSDN博客
一、信号的产生
1.1 信号产生的五种条件
①键盘组合键 :“ctrl + c”或者“ctrl + z”等;
②linux命令: kill - signo processpid , 例:杀掉进程号12345的进程 :kill -9 12345 ;
③系统调用:在自己的代码中调用系统调用接口,给某个进程发信号或者给自己发信号常用的几个接口:
#include <sys/types.h>
#include <signal.h>
int kill( pid_t pid, int sig);
//1. pid : 进程pid,想要发送到哪个进程?
//2. sig :要发送的信号码,想要发送什么信号?
//3. 返回值int,失败返回-1
#include <signal.h>
int raise(int sig);
//1. 给自己发信号,相当于 kill(getpid(),sig);
//2. sig: 想要给自己发送的信号
#include <stdlib.h>
void abort(void);
//1.终止自己
//2.给自己发送6号信号 :SIGABRT
④、异常
异常的过程是:当你的进程被调度的时候,cpu执行你的代码,当cpu执行到某行代码时(例如:除0错误或者对空指针访问等)->
cpu知道自己的运算异常,在cpu的状态寄存器中修改溢出标志位->
同时cpu给OS发送硬件异常错误 ->
OS收到cpu硬件异常的报错后去找到对应的进程 ->
并给这个进程发送对应的异常信号,提示这个进程需要注意 ->
如果这个进程不结束,异常一直没有解决,cpu会一直调度这个进程,cpu会一直给OS发送硬件异常错误,OS会一直给这个进程发信号,进程处于死循环;
⑤软件条件
例如:闹钟接口,每隔一段时间给进程发送闹钟信号;
#include <unistd.h>
unsigned int alarm( unsigned int second);
//1. second:每隔多少秒给进程发信号?
//2. 返回值unsigned int 是上一个闹钟剩余的时间
1.2 理解core dump
打开系统的core dump功能,一旦进程出现异常,OS会将进程在内存中的运行信息,给dump(转储)到进程的当前目录(磁盘)上,形成core.pid文件,(核心转储:core dump);
未来在调试的时候,在core-file 直接定位到代码出错行,这是事后调试的方法(运行出错后再调试);
1.3 深度理解信号的发送和产生
①给进程发信号其实是给进程的PCB发的信号;
②所谓的“发信号”,其本质是OS修改PCB内的一个用来管理信号的位图数据结构,这个数据结构其实是一个int的整形,因为一个int 32bit位刚好可以用来表示1~31号信号,所以所谓的“发信号”其实就是对内核数据的写入操作,把对应位置的0写成1,代表信号产生了;
③信号的产生处理整个过程都是OS在操作,因为OS是所有硬件的管理者,只有OS才能直接访问内核数据结构,普通用户只能调用系统接口,因为操作系统不相信任何用户;
二、信号的保存
2.1、信号为什么要保存?
因为进程收到信号之后,可能不会立即处理,会有一个时间窗口,在这个过程中需要先把信号保存起来;
2.2、信号如何保存?进程如何管理信号?
通过三张表:
①block表,位图数据结构,与每个信号映射对应关系,这张表用来记录某个信号是否被屏蔽,如果被屏蔽把对应信号位置的0改1,当取消屏蔽把对应的1再改为0;
②pending表,位图数据结构,与每个信号映射对应关系,这张表用来记录某个信号是否已经产生,是否已经收到某个信号,注意:如果信号已产生但并未处理即(信号未决),把对应信号位置的0改为1,当这个信号被处理后再从对应的1改为0;
③handler表,指针数组结构,这是一个用来保存对信号的处理的方法对应的指针,当未来处理这个信号的时候,直接调用指针指向的方法;注意:这里的处理方法包括三种:SIG_DFL(默认)、SIG_IGN(忽略)、自定义方法;
2.3、操作这三张表相关的接口
因为这三张表属于内核数据结构,所以用户不能对其直接进行访问或修改操作,只能通过系统调用接口;
数据准备工作:信号集函数:
#include <signal.h>
int sigemptyset( sigset_t * set);
//1. 对信号集清空处理操作
//2. set:sigset_t类型结构,是系统封装的位图结构体
#include <signal.h>
int sigaddset( sigset_t * set, int signum);
//1. 添加信号编号到信号集中
//2. set:系统封装的位图结构体
//3. signum想要添加到信号集中的信号编号(未来对这个信号进行操作)
//4. 返回值int,如果成功返回0否则返回-1
#include <signal.h>
int sigismember(const sigset_t *set ,int signo);
//1. 检测信号signo是否存在内核中的pending表中,存在代表信号产生,信号未决;
//2. set: 系统封装的位图结构体
//3. signo: 需要检查的信号
//4. 返回值int,如果不存在返回0,如果存在返回1,如果失败返回-1
#include<signal.h>
int sigpending(sigset_t * set);
//1. 获取内核中pending表的接口
//2. 这是输出型参数,传入set带出内核中的pending表
//3. set: 系统封装的位图结构体
//4. 返回值int,如果获取失败返回值<0
系统调用接口:
#include <signal.h>
int sigprocmask( int how, const sigset_t*set, sigset_t* oset);
//1.how: 当how是SIG_SETMASK时,将set自定义设置好的数据一个一个设置进进程PCB的管理信号的block表里
//2. set: 自定义设置好的位图结构体,oset: 通过oset保存内核更改前的数据以便日后恢复
//3. 当第二个参数传入oset,第三个参数传入nullptr时,代表的是取消屏蔽信号
三、信号的捕捉
3.1两个信号捕捉接口
①:signal
#include <signal.h>
typedef void(*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
//1. signum: 需要捕捉的信号
//2. handler: 捕捉信号后调用的自定义方法
//3. sighandler_t : 定义的函数指针类型,参数为int,返回值为void
②:sigaction
#include <signal.h>
int sigaction(int signum, struct action * act,struct action* oact);
//1. signum: 需要捕捉的信号
//2. act: 自己创建并设置好的action对象,输入型参数,OS通过act写入到内核数据中
//3. oact: 自己创建初始化好的action对象,输出型参数,通过oact把内核中修改前的数据带出来,以便日后恢复
//4. action: 结构体,成员中包括handler处理方法、sigset_t类型的sa_mask,传入前通过添加sa_mask字段达到信号在处理时屏蔽其它信号的效果;
//5.注意:这个接口需要再传参之前把act、oact创建并初始化好,同时act需要把结构体成员sa_handler(自定义方法)、sa_mask(需要屏蔽的信号)字段设置好(如果你不想屏蔽其它信号可以不设置);
3.2可重入函数理解
例如:当程序运行时向链表头插一个Node1节点,调用insert方法,在方法运行到创建了一个节点,链接到头部,并且正准备让head指向Node1的时候,突然接收到一个信号!这时主函数main会停下来,先执行处理信号的方法,正巧的是处理信号的方法也是insetr即(向链表头插一个节点),那么此时会先运行完处理信号的方法:把Node2头插到链表并把head指向了Node2,处理完后回到主函数main接着刚才的下一步即:让head指向Node1,那么此时Node2无法再被找到!!
简单的代码(不要扣细节,大概意思懂就行):
void insert()
{Node->next=head;//①当main函数执行到这里的时候收到2号信号head=Node;//④回到main刚才被暂停的位置继续往下执行,执行完后Node2没有指针指向它,成为丢失节点!!
}
void handler(int signo)
{list->insert(Node);//③执行inser成功头插并把head指向Node
}
int main()
{signal(2,handler);//② 二号信号被捕获并跳转到自定义的handler方法list->insert(Node1);return 0;
}
重入函数:如果一个函数被重复进入的情况下,出错了或者可能出错那么这个函数为不可重入函数!反之则是可重入函数!!!
3.3、volatile
volatile的作用:防止编译器过度优化,保持内存可见性!!
代码:
#include <iostream>
using namespace std;
#include <unistd.h>
#include <signal.h>
int flag=0;
void handler(int signo)
{flag=1;cout<<" signo is: "<<signo<<endl;
}
int main()
{signal(2,handler);while(!flag);cout<<"i am process !! process id:"<<getpid()<<endl;return 0;
}
编译器优化后cup没从内存读取flag不会运行下一句:
加上volatile后再编译运行:
cpu从内存中读取flag,循环条件判断失败,指向下一条语句;
四、基于信号方式的进程等待
4.1、当子进程退出时会给父进程发一个信号
我们捕获这个信号然后自定义处理方法去等待回收这个进程,而父进程继续做自己的事:
#include <sys/wait.h>
#include <iostream>
using namespace std;
#include <unistd.h>
void handler(int signo)
{pid_t id= waitpid(-1,nullptr,0);if(id>0){cout<<"waitprocess success!! process id:"<<id<<endl;}
}
int main()
{signal(2,handler);pid_t id=fork();if(id==0){//childcout<<"i am a child process ,process id is: "<<getpid()<<" ppid is:"<<getppid()<<endl;exit(0);}//fartherwhile(true){cout<<"i am farther process!!"<<endl;sleep(1);}return 0;
}
运行:
发送2号信号:
4.2 如果十个进程同时结束呢?或者结束一半后再结束另一半呢?
此时进程在正在执行信号处理方法,会把同信号屏蔽掉,那怎么办?
用循环非阻塞轮询:
一秒钟创建10个进程并处于僵尸状态:
发送2号信号:
成功回收完所有子进程!!
4.3 如果不需要获取子进程退出状态,不想自己等待的话,还有一个办法能让子进程退出时自动被回收掉
捕获17号信号(子进程结束时给父进程发送的信号),并把方法设置为SIG_IGN(忽略):
子进程被自动回收,监视窗口没有Z状态的进程,且只有一个父进程在跑:
今天的分享就到这里,如果对你有所帮助麻烦点赞收藏+关注哦!!谢谢!!!
咱下期见!!!
相关文章:

Linux 信号终篇(总结)
前文:本文是对信号从产生到被处理的过程中的概念和原理的总结,如果想了解具体实现,请查看前两篇博客:Linux 信号-CSDN博客、Linux 信号(下篇)-CSDN博客 一、信号的产生 1.1 信号产生的五种条件 ①键盘组…...
使用Kotlin Flow实现Android应用的响应式编程
在Android应用中使用Kotlin Flow实现响应式编程可以分为以下步骤,结合最佳实践和生命周期管理: 1. 添加依赖 在build.gradle中确保包含协程和生命周期相关依赖: dependencies {implementation("org.jetbrains.kotlinx:kotlinx-corouti…...
react中的用法——setDisabled dva dispatch effects
setDisabled 在react中,setDisabled通常是指通过状态管理来控制某个组件(如按钮、输入框等)的禁用状态。虽然react本身没有内置的setDisabled方法,但你可以使用useState钩子来实现类似的功能。以下是一个简单的示例,展…...
(九)Java Object类的使用全面解析
一、Object类概述 1.1 Object类在Java中的地位 在Java语言中,Object类是所有类的超类,位于类继承树的顶端。它是Java类层次结构中的根类,每个类都直接或间接继承自Object类。当我们定义一个类时,如果没有明确使用extends关键字指…...

LVGL对象(Objects)
文章目录 🧱 一、LVGL 中的对象(lv\_obj)🔹 lv\_obj\_t 的作用 🧩 二、对象的分类结构(类比继承)🧰 三、对象的创建与销毁✅ 创建对象示例:创建一个按钮❌ 删除对象 &…...
自然语言到 SQL 转换:开启智能数据库交互新时代
引言 随着人工智能技术的飞速发展,自然语言处理(NLP)领域取得了显著的进步。其中,将自然语言转换为 SQL 语句的技术尤为引人注目。这种技术能够帮助用户,尤其是那些不熟悉 SQL 的业务人员,快速从数据库中获…...

服务器配置错误导致SSL/TLS出现安全漏洞,如何进行排查?
SSL/TLS 安全漏洞排查与修复指南 一、常见配置错误类型 弱加密算法与密钥问题 使用弱密码套件(如DES、RC4)或密钥长度不足(如RSA密钥长度<2048位),导致加密强度不足。 密钥管理不当(如私钥未加密存…...

路由重发布
路由重发布 实验目标: 掌握路由重发布的配置方法和技巧; 掌握通过路由重发布方式实现网络的连通性; 熟悉route-pt路由器的使用方法; 实验背景:假设学校的某个分区需要配置简单的rip协议路由信息,而主校…...

C++修炼:stack和queue
Hello大家好!很高兴我们又见面啦!给生活添点passion,开始今天的编程之路! 我的博客:<但凡. 我的专栏:《编程之路》、《数据结构与算法之美》、《题海拾贝》、《C修炼之路》 欢迎点赞,关注&am…...
【软件工程】基于频谱的缺陷定位
基于频谱的缺陷定位(Spectrum-Based Fault Localization, SBFL)是一种通过分析程序执行覆盖信息(频谱数据)来定位代码中缺陷的方法。其核心思想是:通过测试用例的执行结果(成功/失败)和代码覆盖…...

【计算机视觉】优化MVSNet可微分代价体以提高深度估计精度的关键技术
优化MVSNet可微分代价体以提高深度估计精度的关键技术 1. 代价体基础理论与分析1.1 标准代价体构建1.2 关键问题诊断 2. 特征表示优化2.1 多尺度特征融合2.2 注意力增强匹配 3. 代价体构建优化3.1 自适应深度假设采样3.2 可微分聚合操作改进 4. 正则化与优化策略4.1 多尺度代价…...
为什么tcp不能两次握手
TCP **不能用“两次握手”**的根本原因是:两次握手无法确保双方“都知道”连接是可靠建立的,容易引发“旧连接请求”造成错误连接。 🔁 先看标准的 三次握手(3-Way Handshake)流程 客户端 服务器| …...
常见音频主控芯片以及相关厂家总结
音频主控芯片是音频设备(如蓝牙耳机、音箱、功放等)的核心组件,负责音频信号的解码、编码、处理和传输。以下是常见的音频主控芯片及其相关厂家,按应用领域分类: 蓝牙音频芯片 主要用于无线耳机、音箱等设备࿰…...
掌握 Kubernetes 和 AKS:热门面试问题和专家解答
1. 在 AKS(Azure Kubernetes 服务)中,集群、节点、Pod 和容器之间的关系和顺序是什么? 在 AKS(Azure Kubernetes 服务)中,集群、节点、Pod 和容器之间的关系和顺序如下: 集群&#…...
【MyBatis-7】深入理解MyBatis二级缓存:提升应用性能的利器
在现代应用开发中,数据库访问往往是性能瓶颈之一。作为Java生态中广泛使用的ORM框架,MyBatis提供了一级缓存和二级缓存机制来优化数据库访问性能。本文将深入探讨MyBatis二级缓存的工作原理、配置方式、使用场景以及最佳实践,帮助开发者充分利…...
5.9-selcct_poll_epoll 和 reactor 的模拟实现
5.9-select_poll_epoll 本文演示 select 等 io 多路复用函数的应用方法,函数具体介绍可以参考我过去写的博客。 先绑定监听的文件描述符 int sockfd socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in serveraddr; memset(&serveraddr, 0, sizeof(struc…...
《算法导论(第4版)》阅读笔记:p17-p27
《算法导论(第4版)》学习第 10 天,p17-p27 总结,总计 11 页。 一、技术总结 1. insertion sort (1)keys The numbers to be sorted are also known as the keys(要排序的数称为key)。 第 n 次看插入排序,这次有两个地方感触比较深&#…...

软考错题集
一个有向图具有拓扑排序序列,则该图的邻接矩阵必定为()矩阵。 A.三角 B.一般 C.对称 D.稀疏矩阵的下三角或上三角部分包含非零元素,而其余部分为零。一般矩阵这个术语太过宽泛,不具体指向任何特定性 质的矩阵。对称矩阵…...

T2I-R1:通过语义级与图像 token 级协同链式思维强化图像生成
文章目录 速览摘要1 引言2 相关工作统一生成与理解的 LMM(Unified Generation and Understanding LMM.)用于大型推理模型的强化学习(Reinforcement Learning for Large Reasoning Models.)3 方法3.1 预备知识3.2 语义级与令牌级 CoT语义级 CoT(Semantic-level CoT)令牌级…...

Dockers部署oscarfonts/geoserver镜像的Geoserver
Dockers部署oscarfonts/geoserver镜像的Geoserver 说实话,最后发现要选择合适的Geoserver镜像才是关键,所以所以所以…🐷 推荐oscarfonts/geoserver的镜像! 一开始用kartoza/geoserver镜像一直提示内存不足,不过还好…...
【脑机接口临床】脑机接口手术的风险?脑机接口手术的应用场景?脑机接口手术如何实现偏瘫康复?
脑机接口的应用 通常对脑机接口感兴趣的两类人群,一类是适应症患者 ,另一类是科技爱好者。 1 意念控制外部设备 常见的外部设备有:外骨骼、机械手、辅助康复设备、电刺激设备、电脑光标、轮椅。 2 辅助偏瘫康复或辅助脊髓损伤患者意念控制…...

扩增子分析|微生物生态网络稳定性评估之鲁棒性(Robustness)和易损性(Vulnerability)在R中实现
一、引言 周集中老师团队于2021年在Nature climate change发表的文章,阐述了网络稳定性评估的原理算法,并提供了完整的代码。自此对微生物生态网络的评估具有更全面的指标,自此网络稳定性的评估广受大家欢迎。本系列将介绍网络稳定性之鲁棒性…...
Client 和 Server 的关系理解
client.py 和 server.py 是基于 MCP(Multi-Component Protocol)协议的客户端-服务端架构,二者的关系如下: 1. 角色分工 server.py:服务端,负责注册和实现各种“工具函数”(如新闻检索、情感分…...

【含文档+PPT+源码】基于微信小程序的社区便民防诈宣传系统设计与实现
项目介绍 本课程演示的是一款基于微信小程序的社区便民防诈宣传系统设计与实现,主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含:项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署运行本套…...
Kafka的核心组件有哪些?简要说明其作用。 (Producer、Consumer、Broker、Topic、Partition、ZooKeeper)
Kafka 核心组件解析 1. 基础架构图解 ┌─────────┐ ┌─────────┐ ┌─────────┐ │Producer │───▶ │ Broker │ ◀─── │Consumer │ └─────────┘ └─────────┘ └────────…...
Java中对象集合转换的优雅实现【实体属性范围缩小为vo】:ListUtil.convert方法详解
1.业务场景 在开发电商系统时,我们经常需要处理订单信息的展示需求。例如:订单详情页需要显示退款信息列表,而数据库中存储的RefundInfo实体类包含敏感字段,直接返回给前端存在安全风险。此时就需要将RefundInfo对象集合转换为Or…...

【MySQL】存储引擎 - ARCHIVE、BLACKHOLE、MERGE详解
📢博客主页:https://blog.csdn.net/2301_779549673 📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! &…...

代码随想录第41天:图论2(岛屿系列)
一、岛屿数量(Kamacoder 99) 深度优先搜索: # 定义四个方向:右、下、左、上,用于 DFS 中四向遍历 direction [[0, 1], [1, 0], [0, -1], [-1, 0]]def dfs(grid, visited, x, y):"""对一块陆地进行深度…...
Vue插槽(Slots)详解
文章目录 1. 插槽简介1.1 什么是插槽?1.2 为什么需要插槽?1.3 插槽的基本语法 2. 默认插槽2.1 什么是默认插槽?2.2 默认插槽语法2.3 插槽默认内容2.4 默认插槽实例:创建一个卡片组件2.5 Vue 3中的默认插槽2.6 默认插槽的应用场景 …...
中国古代史1
朝代歌 三皇五帝始,尧舜禹相传。 夏商与西周,东周分两段。 春秋和战国,一统秦两汉。 三分魏蜀吴,二晋前后延。 南北朝并立,隋唐五代传。 宋元明清后,皇朝至此完。 原始社会 元谋人,170万年前…...