Linux : 内核中的信号捕捉

目录

一 前言

 二 信号捕捉的方法

1.sigaction()​编辑

2. sigaction() 使用 

 三  可重入函数

四 volatile 关键字 


一 前言

如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。在Linux: 进程信号初识-CSDN博客 这一篇中已经学习到了一种信号捕捉的调用接口:signal(),为了深入理解操作系统内核中的信号捕获机制,我们今天再来看一个接口:sigaction()。


 二 信号捕捉的方法

1.sigaction()

🍪: sigaction 作为一个系统调用,用于修改和/或查询信号的处理方式。相较于 signal 函数来说,它提供了对信号处理函数更精细的控制。

  • int signum:该参数需要传入指定的进程信号,表示要捕捉的信号。
  • const struct sigaction *act:该参数与函数同名,是一个结构体指针。

  1. void (*sa_handler)(int)  :此成员的含义其实就是自定义处理信号的函数指针;
  2. void (*sa_sigcation)(int, siginfo_t *, void *): 此成员也是一个函数指针. 但是这个函数的意义是用来 处理实时信号的, 本节不考虑分析。
  3. sigset_t sa_mask: 此成员是一个信号集。
  4. int sa_flags:此成员包含着系统提供的一些选项, 本篇文章使用中都设置为0
  5. 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 关键字的作用就是保持内存可见性

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

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

相关文章

分布式id生成算法(雪花算法 VS 步长id生成)

分布式ID生成方案详解:雪花算法 vs 步长ID 一、核心需求 全局唯一性:集群中绝不重复有序性:有利于数据库索引性能高可用:每秒至少生成数万ID低延迟:生成耗时<1ms二、雪花算法(Snowflake) 1. 数据结构(64位) 0 | 0000000000 0000000000 0000000000 0000000000 0 |…

函数式编程在 Java:Function、BiFunction、UnaryOperator 你真的会用?

大家好&#xff0c;我是你们的Java技术博主&#xff01;今天我们要深入探讨Java函数式编程中的几个核心接口&#xff1a;Function、BiFunction和UnaryOperator。很多同学虽然知道它们的存在&#xff0c;但真正用起来却总是不得要领。这篇文章将带你彻底掌握它们&#xff01;&am…

x265 编码器中运动搜索 ME 方法对比实验

介绍 x265 的运动搜索方法一共有 6 种方法,分别是 DIA、HEX、UMH、STAR、SEA、FULL。typedef enum {X265_DIA_SEARCH,X265_HEX_SEARCH,X265_UMH_SEARCH,X265_STAR_SEARCH,X265_SEA,X265_FULL_SEARCH } X265_ME_METHODS;GitHub

2025.4.8 dmy NOI模拟赛总结(转化贡献方式 dp, 交互(分段函数找断点),SAM上计数)

文章目录 时间安排题解T1.搬箱子(dp&#xff0c;转化贡献方式)T2.很多线。(分段函数找断点)T3.很多串。(SAM&#xff0c; 计数) 时间安排 先写了 T 3 T3 T3 60 p t s 60pts 60pts&#xff0c;然后剩下 2.5 h 2.5h 2.5h 没有战胜 T 1 T1 T1 40 p t s 40pts 40pts。 总得分…

ZYNQ笔记(四):AXI GPIO

版本&#xff1a;Vivado2020.2&#xff08;Vitis&#xff09; 任务&#xff1a;使用 AXI GPIO IP 核实现按键 KEY 控制 LED 亮灭&#xff08;两个都在PL端&#xff09; 一、介绍 AXI GPIO (Advanced eXtensible Interface General Purpose Input/Output) 是 Xilinx 提供的一个可…

CSP认证准备第二天-第36/37次CCF认证

第37次CCF认证-第三题 主要是间接赋值比较难。 自己编写的代码如下&#xff0c;但是有问题&#xff0c;没有解决间接赋值的问题&#xff0c;可以参考一下deepseek的回答。 #include <iostream> #include <bits/stdc.h> using namespace std; long long n,x; char …

Kotlin与HttpClient编写视频爬虫

想用Apache HttpClient库和Kotlin语言写一个视频爬虫。首先&#xff0c;我需要确定用户的具体需求。视频爬虫通常涉及发送HTTP请求&#xff0c;解析网页内容&#xff0c;提取视频链接&#xff0c;然后下载视频。可能需要处理不同的网站结构&#xff0c;甚至可能需要处理动态加载…

FFMEPG常见命令查询

基本参数 表格1&#xff1a;主要参数 参数说明-i设定输入流-f设定输出格式(format) 高于后缀名-ss开始时间-t时间长度codec编解码 表格2&#xff1a;音频参数 参数说明-aframes设置要输出的音频帧数-f音频帧深度-b:a音频码率-ar设定采样率-ac设定声音的Channel数-acodec设定…

Java-对比两组对象找出发生变化的字段工具-支持枚举映射-支持时间-支持显示对应字段中文描述-嵌套list等场景

实体字段比较器&#xff08;对比两组对象找出发生变化的字段工具类开发&#xff09; 支持枚举映射 支持时间 支持显示对应字段中文描述 支持嵌套list等场景 下载地址&#xff1a; Java-对比两组对象找出发生变化的字段工具-支持枚举映射-支持时间-支持显示对应字段中文描述-嵌…

15. git push

基本概述 git push 的作用是&#xff1a;把本地分支的提交推送到远程仓库。推送分支需要满足快进规则&#xff08;Fast-Forward&#xff09;&#xff0c;即远程分支的最新提交必须是本地分支的直接祖先&#xff0c;这个是通过哈希值值进行判断的。 基本用法 1.完整格式 git…

训练数据清洗(文本/音频/视频)

多数据格式的清洗方法 以下是针对多数据格式清洗方法的系统性总结&#xff0c;结合Python代码示例&#xff1a; 一、数据清洗方法总览&#xff08;表格对比&#xff09; 数据类型核心挑战关键步骤常用Python工具文本非结构化噪声去噪→分词→标准化→向量化NLTK, SpaCy, Jie…

Python标准库json完全指南:高效处理JSON数据

一、json库概述 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式&#xff0c;Python的json模块提供了JSON数据的编码和解码功能。该模块可以将Python对象转换为JSON字符串&#xff08;序列化&#xff09;&#xff0c;也可以将JSON字符串转换为Python对象&#xf…

微软推出首款量子计算芯片Majorana 1

全球首款拓扑架构量子芯片问世&#xff0c;2025年2月20日&#xff0c;经过近20年研究&#xff0c;微软推出了首款量子计算芯片Majorana 1&#xff0c;其宣传视频如本文末尾所示。 微软表示&#xff0c;开发Majorana 1需要创造一种全新的物质状态&#xff0c;即所谓的“拓扑体”…

【QT】QT中的文件IO

QT中的文件IO 一、有关文件IO的类二、步骤1、定义QFile的对象,与要读写的文件绑定在一起2、打开文件3、读写文件1&#xff09;读取文件2&#xff09;写入文件 4、关闭文件5、示例代码&#xff1a; 三、QString和QByteArray之间的转换1、方法2、示例代码&#xff1a; 四、QFileI…

Nginx 499 错误的原因及解决方法

Nginx 499 错误的原因及解决方法 原因 客户端超时&#xff1a; 客户端在等待服务器响应时超时&#xff0c;导致连接被关闭。 解决方法&#xff1a;优化服务端响应时间&#xff0c;或调大客户端的连接超时时间。 服务端响应过慢&#xff1a; 后端服务处理请求时间过长。 解决方法…

Smith-Waterman 算法(C++实现)

本文实现Smith-Waterman 算法案例&#xff0c;用于局部序列比对。该算法是生物信息学中用于寻找两个 DNA、RNA 或蛋白质序列之间最优局部比对的经典算法&#xff0c;广泛应用于序列相似性分析和功能预测。 问题描述 给定两个生物序列 seq1 和 seq2&#xff0c;如何找到它们的最…

安卓玩机工具-----安卓机型通用 无损备份与恢复数据的工具BackupToolkit 操作过程

常规安卓机型数据备份与恢复的方法及工具 安卓设备的数据备份与恢复是保护个人数据的重要手段之一。以下是几种常用的方法和工具&#xff1a; 方法一&#xff1a;利用内置的云服务进行备份 许多安卓设备提供了内置的云服务&#xff0c;例如华为手机可以通过“华为云空间”来…

oracle 动态性能视图

Oracle 数据库中的 V$SQLAREA 是一个动态性能视图&#xff08;Dynamic Performance View&#xff09;&#xff0c;用于记录共享池&#xff08;Shared Pool&#xff09;中所有 SQL 语句的统计信息。每个 SQL 语句在共享池中存储为一个游标&#xff08;Cursor&#xff09;&#x…

OceanBase V4.3.5 上线全文索引功能,让数据检索更高效

近日&#xff0c;OceanBase 4.3.5 BP1 版本正式推出了企业级全文索引功能。该版本在中文分词、查询效率及混合检索能力上进行了全面提升。经过自然语言模式和布尔模式在不同场景下的对比测试&#xff0c;OceanBase 的全文索引性能明显优于 MySQL。 点击下载 OceanBase 社区版…

海康摄像头AI报警、移动侦测报警等通过Ehome/ISUP协议上报到LiveNVR流媒体平台时如何进行报警配置

海康摄像头AI报警、移动侦测报警等通过Ehome/ISUP协议上报到LiveNVR流媒体平台时如何进行报警配置 1、LiveNVR介绍2、如何配置海康摄像头、录像机通过Ehome/ISUP注册到LiveNVR设备 EHOME 接入配置示例设备 ISUP 接入配置示例直播流接入类型 海康ISUP海康 ISUP 设备ID启用保存 3…