Linux——信号(3)

经过前两部分的介绍,我们现在已经认识了信号是如何产生的,并且知道无
论信号是如何产生的,最终只能由操作系统来对特定进程发送信号,而发送
信号其实也就是写信号,往内核数据结构(pending表)中写。我们也认识了
信号的几种状态,也直到了关于信号的一些操作,但是我们之前说过,当信
号处于信号未决状态时,进程会在合适的时候对信号进行处理,那么这个“合
适”的时候是什么时候呢?这就是我们今天要谈论的主要问题。

1. 信号的处理和捕获

我们现在知道了,信号的处理方式有三种:默认处理方式,忽略和自定义捕捉。那么接着上面的话题,“合适”的时候究竟是什么时候呢?这里直接给出答案:
进程在从内核态到用户态的时候会进行信号的检测并处理。

a. 小铺垫

从这里就出现了两个新的名词:用户态和内核态。它们俩代表了计算机中进程的不同的身份,从名字来看,内核态所能使用的资源一定是多于用户态的,事实也是正如你所想的。
那么什么是内核态,什么是用户态呢?
我们的代码在编写号的时候,难免会使用到系统调用,访问到系统的内核级资源(例如管道资源,task_struct结构体等等),那么对于我们用户来说,内核级资源是我们普通用户能够访问的吗?答案是肯定不能的,所以当我们的进程调用系统调用访问系统内核级资源的时候,进程要进行从用户态到内核态的身份的转换。比如我们的信号:
在这里插入图片描述
其中我们使用signal函数进行自定义捕捉然后向进程发送对应信号时,我们的进程一定是经过了从用户到内核的。
我们以前看到地址空间的时候可能看到的是这样的分布:
在这里插入图片描述

我们应该都直到用户区的各种分区代表的什么意思,但是内核数据区我们好像不是那么熟悉。
那你有没有想过我们调用系统调用的时候,这个系统调用在哪里呢?系统调用又不是我们写的,系统调用也肯定不是什么库函数那自然也不在共享区中,我们也没有什么系统库这样的说法。
此时地址空间这幅图里面有一个区域:内核数据区,地址空间中的内核数据区有自己独立的页表会跟内存中的操作系统进行映射。内存中的操作系统有一部分就是系统调用,当然除了系统调用外,还有各种管理体系,调度方法,内核数据结构等,这些都会通过内核级页表来跟地址空间中的内核数据区进行映射(要知道操作系统也是一个软件,也是一个进程,它也有代码和数据)。
那么此时就说得通了,调用系统调用就跟调用库函数一样,也是在地址空间中进行的。
在32位机器下地址空间的大小是4GB其中用户区占[0, 3GB],内核占[3GB, 4GB]。
在这种机制下,我们进程的代码所有的执行都是在资金的地址空间进行调用,跳转,和返回。
此外这么做的话,要知道计算机是不论何时都会有进程在运行,系统中又不止一个进程,那么我们要进行我们的文件系统,驱动管理,调度方法等等直接就可以通过当前调度进程的地址空间的内核区去进行找对应的方法然后调用不就行了啊。比如时钟中断到了需要进行进程调度切换的时候,此时我们直接就可以在当前要被剥离的进程上的地址空间中的内核区去调用调度方法,直接就可以实现进程间切换。这样的机制可以让CPU随时都可以找到操作系统
当我们的进程从用户区到内核区的跳转其实就已经让进程的身份发生变化了。当再次回到用户区执行我们的主代码时,又会从内核态转变位用户态,此时操作系统就会对该进程进行信号的检测以及处理。
那么就有人问了,CPU是怎么知道该进程此时是属于内核态还是用户态啊,该不该让它进入内核啊,其实CPU中有一个寄存器叫做CS,它的低两位会表示当前进程是用户态还是内核态,在Linux操作系统中1表示用户,3表示内核,就算两个位能够表示四种状态,但确实设置的就只有两种用户态和内核态,这样,CPU就能够识别进程的状态了。
这里有个小知识点:我们知道CPU运行我们的代码和运算我们的数据时使用的都是虚拟地址,然后通过MMU转化为物理地址区进行操作的,而页表的地址不是虚拟地址而是物理地址,这个结果也是不令人意外的。

b. 信号的处理和捕捉

经过上面的铺垫之后,我们再来认识“合适”的时候捕获并处理信号。
那么我们发送给进程信号之后,当进程执行完系统调用之后,会从内核态转变为用户态,这个时候,操作系统首先会检测信号(pending表),检测到有信号(该信号没有被阻塞)之后就会进行对信号的处理。
如果被阻塞了,那么操作系统会直接返回,然后继续执行主代码。
在处理的时候默认处理和忽略自不用多说,这些都是内核的事情,它们会做好,它们会在做完之后,直接返回然后继续执行我们的代码。
那假如是用户自定义的呢?这里给出大致的流程图:
在这里插入图片描述
当自定义捕捉执行完之后,它该干什么呢?直接回到主代码接着执行吗?它怎么能够找到主代码在哪里呢?我们的函数中可没有提供主代码的地址。所以它会再次回到内核中,至于回到内核的方式则是调用系统调用:sigreturn
这个函数能够将我们重新带回到主代码上。
在这里插入图片描述
那么疑问就又来了,它都找不到主代码在哪里,我们又没有调用这个系统调用,它又凭什么能够找到这个函数呢?
要知道当调用函数的时候,是会在栈区建立栈帧的,而当函数有返回值时,还会通过压栈的方式将返回值存入到寄存器中,而操作系统正是利用了这一特点,将该系统调用的地址作为我们自定义捕捉函数的返回值,然后就能调用了。当调用了这个函数之后,我们的进程再次回到内核,然后再返回执行我们的主代码:
在这里插入图片描述
此刻信号的处理和捕获就完成了。
现在还有一些小问题:
自定义捕捉的函数是以用户态来执行还是以内核态来执行?
其实通过上面的了解后,它是以用户态进行的。这么做的主要的目的就是怕当以内核态执行自定义捕捉函数时,该函数中会又恶意修改内核数据的代码,这样的后果是不堪设想的。
用户态和内核态之间的切换只能通过系统调用吗?
我们有的代码中可能压根就不会使用系统调用,如果假如里面只进行一些运算呢?向这个进程发送信号就不起作用了吗?这样的现象显然是不合理的。要知道我们的进程调度和进程的切换机制。当时钟中断之后该进程的时间片到了,需要替换掉该进程,而当再次调度到该进程的时候,经过上面的介绍进程的切换调度方法是通过地址空间中的内核区找到的,而当调度完成之后开始运行这个进程,是不是也从内核态到用户态了呢?所以用户态和内核态之间的切换不仅仅是只能通过系统调用的,还有时钟中断。当然还不只有时钟中断,这里不再介绍了。
此时就可以将我们信号的处理和捕获进行大致的抽象了:
在这里插入图片描述
那处理方式是自定义捕捉的的话,两种状态的切换就进行了四次,再对这个过程进行抽象:
在这里插入图片描述

c. 信号的处理的特点

1) . sigaction

信号在进行递达时,如果此时有相同类型的信号再次未决的话,则这个信号会被阻塞,直到该次信号递达完成之后,在进行下一次的递达,这么做的目的也是防止进程重复进入相同的自定义捕捉中。
我们可以写一段这样的代码来验证:
在这里插入图片描述

在这里插入图片描述
可以看到当我们发送二号信号,进程开始处理二号信号时,我们再次发送二号信号该进程的二好信号是未决的,但是没有递达,直到第一次信号递达完成之后,立即开始第二次的信号递达,这里也有一个结论:被阻塞的信号当解除阻塞之后,该进程要立即处理
而此时我们又要介绍一个关于这方面的系统调用:
在这里插入图片描述
这个函数可以看到它的参数长得跟我们前面的sigprocmask有点像,但是这里使用参数的是结构体,如果我们只关注这个结构体的第一个参数的话,这个函数跟signal的效果是一样的:
在这里插入图片描述

在这里插入图片描述
我们要介绍的是这个结构体的另一个成员sa_mask,当看到它的类型和名字其实就可以猜个差不多了,这个成员的作用是,当进行信号的递达时,可以自定义阻塞想阻塞的信号,这个时候我们在使用一下这个接口,同时阻塞三号信号:
在这里插入图片描述

在这里插入图片描述
可以看到确实是阻塞住了。

2) .多信号的处理

以上谈论信号的捕捉和处理都是单个信号的情况,那么就有一个问题,如果是多信号的情况下的话,究竟是一个一个执行呢(中间会进行用户态和内核态的切换)?还是一次性全执行呢?
我们也可以写代码进行验证:
在这里插入图片描述

在这里插入图片描述可以看到是一并处理。但是看到它的处理顺序是无序的,这也说明信号的处理也是有优先级的

2. 信号的其他补充

a. 可重入函数

我们现在来想这么一个问题:
假如我们有一个链表,现在在主代码中要进行头插一个节点node1,而在进行插入的时候,刚好有一个信号发过来,也刚好立即处理信号,而信号的自定义捕捉中,也调用了头插函数,头插了一个节点node2.那么此时就会出现这样的情况:
在这里插入图片描述

就会出现内存泄漏。

你可以想象为主代码和自定义捕捉是两个执行流(虽然这是多进程和多线程的知识),像这样有两个执行流,在一个执行流还没结束的时候另一个执行流也进入了这个函数,这种现象叫做重入,而因为这种重入现象造成混乱(例如内存泄漏)的函数叫做不可重入函数,反之则称为可重入函数。
需要注意的是,不可重入/可重入这只是一个函数的特性,我们无法评判这个函数的好坏,所以不可重入函数它不是不可以使用的函数,它只是在多执行流中会产生混乱,但是单执行流中是正常的。
一般来说,拥有以下任一特点,都是不可重入函数:

调用了malloc或free,因为malloc也是用全局链表来管理堆的。
调用了标准I/O库函数。标准I/O库的很多实现都以不可重入的方式使用全局数据结构。

b. volatile关键字

volatile是C语言中的一个关键字,它可以使得变量保持内存可见性。我们可以使用代码来验证这一点:
在这里插入图片描述
在这里插入图片描述
在使用g++/gcc编译器时默认是对我们形成的可执行没有优化的,使用-O+特定数字选项可以对我们要形成的可执行进行优化:
在这里插入图片描述
我们看到这个程序还在运行,下面我来解释其中的原理:
我们的主代码中并没有对这个flag进行算数修改,而我们又对我们的可执行进行了优化,导致在启动我们的程序的时候flag这个变量会被加载到寄存器中成为一个寄存器变量,while循环判断的就是这个寄存器中的变量,而我们发送信号进入自定义捕捉中将flag修改,这是将内存中的flag修改了。所以while还是会进入死循环,而解决方法就是使用volatile关键字:
在这里插入图片描述

在这里插入图片描述
保证了flag的内存可见性,而保证的对象就是CPU。

c. SIGCHLD

我们在研究父子进程的时候,僵尸进程是非常恐怖的事情,所以回收子进程变得尤为重要。其实子进程再退出的时候会给父进程发送一个信号那就是17号信号SIGCHLD:
在这里插入图片描述
通过代码我们可以验证这一点:
在这里插入图片描述
在这里插入图片描述
既然当子进程退出的时候会向父进程发送信号的话我们可以利用这一特点,在自定义捕捉中进行子进程的回收:
在这里插入图片描述
这样我们也可以进行多个子进程的回收:
在这里插入图片描述

在这里插入图片描述
但其实,我们也可以这样:
在这里插入图片描述
将17号信号手动设置成忽略,这样在Linux操作系统下,系统在子进程退出后会自动回收子进程。这样的方式回收子进程确实方便,但是却无法查看子进程的退出码和信号,所以子进程的回收需要依据具体场景使用。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

第3.1章:StarRocks数据导入——Insert into 同步模式

一、概述 在StarRocks中,insert的语法和mysql等数据库的语法类似,并且每次insert into操作都是一次完整的导入事务。 主要的 insertInto 命令包含以下两种: insert into tbl select ...insert into tbl (col1, col2, ...) values (1, 2, ...…

Oracle 如何提高空间使用率?

一,行迁移和行链接。 oracle尽量保证一行的数据能够放在同一个数据块当中,有的时候行会发生行迁移和行链接。 行链接 :有一个列的字段是大对象(long,longlong)一行占的数据一整个块都放不下,则…

OpenCV统计函数之minMaxLoc和meanStdDev

在OpenCV中,minMaxLoc和meanStdDev是两个用于统计图像或数组中元素的基本特性的函数。这些统计函数对于图像处理、特征提取和数据分析非常有用。 minMaxLoc minMaxLoc函数用于查找数组或图像中的最小值和最大值,并可选地返回这些值的位置。这在处理图像…

【漏洞复现】大华DSS视频管理系统信息泄露漏洞

Nx01 产品简介 大华DSS数字监控系统是一个在通用安防视频监控系统基础上设计开发的系统,除了具有普通安防视频监控系统的实时监视、云台操作、录像回放、报警处理、设备治理等功能外,更注重用户使用的便利性。 Nx02 漏洞描述 大华DSS视频管理系统存在信…

.NET 9 首个预览版发布:瞄准云原生和智能应用开发

前言 前不久.NET团队发布了.NET 9 的首个预览版,并且分享.NET团队对 .NET 9 的初步愿景,该愿景将于今年年底在 .NET Conf 2024 上发布。其中最重要的关注领域是:云原生和智能应用开发。 云原生开发人员平台 过去几年,.NET团队一直…

探索 LRU 算法的缺陷与解决方案

LRU算法 Linux 的 Page Cache 和 MySQL 的 Buffer Pool 的大小是有限的,并不能无限的缓存数据,对于一些频繁访问的数据我们希望可以一直留在内存中,而一些很少访问的数据希望可以在某些时机可以淘汰掉,从而保证内存不会因为满了而…

conda 进入python环境里pip install安装不到该环境或不生效

参考:https://blog.csdn.net/weixin_47834823/article/details/128951963 https://blog.51cto.com/u_15060549/4662570?loginfrom_csdn 1、直接进入python Scripts目录下安装 cmd打开运行窗口,cd切换路径至指定虚拟环境下的Scripts路径后再pip安装 擦…

08-静态pod(了解即可,不重要)

我们都知道,pod是kubelet创建的,那么创建的流程是什么呐? 此时我们需要了解我们k8s中config.yaml配置文件了; 他的存放路径:【/var/lib/kubelet/config.yaml】 一、查看静态pod的路径 [rootk8s231 ~]# vim /var/lib…

SQL笔记-多表查询(合并记录新增字段)

比如要统计2张表的所有数据,这两张表无关联关系,统计的数据需要在同一行: SELECT (SELECT COUNT(*) FROM reptile_csdn_article) AS table1_count, (SELECT COUNT(*) FROM reptile_tag_type) AS table2_count 运行截图如下: 大于…

构造函数,原型,实例,类的关系整理

视频来源js原型链、构造函数和类_哔哩哔哩_bilibili 如视频所说,构造函数的prototype指向原型,实例化的对象的__proto__指向原型,原型通过constructor指向构造函数,正如class里面的constructor方法就相当于Person构造函数一样&am…

Vue3实现带动画效果的tab栏切换

效果图如下所示&#xff1a; 实现思路&#xff1a; 其实很简单 1、首先切换tab栏时tab标签激活下标与对应显示内容下标要一致。 2、其次点击tab栏切换时更新下标 3、最后就是css添加动画效果 这样就了&#xff01;&#xff01;&#xff01; 上全部代码 <template><…

C++入门 上(命名空间 缺省参数 函数重载)

C入门 上 命名空间命名空间定义命名空间使用 C输入输出缺省参数缺省参数概念缺省参数分类 函数重载函数重载概念C支持函数重载的原理--名字修饰 命名空间 在C/C中&#xff0c;变量、函数和后面要学到的类都是大量存在的&#xff0c;这些变量、函数和类的名称将都存在于全局作用…

前缀和 K倍区间

思路&#xff1a;统计相加后余数数组对应的数&#xff0c;就是k倍区间对应的个数 import java.util.*; public class Main{public static void main(String[] args){Scanner scnew Scanner(System.in);int nsc.nextInt();int msc.nextInt();int f[]new int[m];f[0]1;long sum0;…

C# OpenCvSharp DNN Image Retouching

目录 介绍 模型 项目 效果 代码 下载 C# OpenCvSharp DNN Image Retouching 介绍 github地址&#xff1a;https://github.com/hejingwenhejingwen/CSRNet (ECCV 2020) Conditional Sequential Modulation for Efficient Global Image Retouching 模型 Model Properti…

小型洗衣机什么牌子好?四大超强实力内衣洗衣机整理

如果你对于内衣物的卫生追求比较高&#xff0c;又不想手洗内衣&#xff0c;觉得太耽误时间&#xff0c;那就选购一台小型洗衣机吧&#xff01;其特色的高温洗护程序高效杀灭细菌&#xff0c;呵护健康&#xff0c;同时又能带来如手洗般的洁净&#xff0c;还能很好的保护衣物不被…

RuntimeError: CUDA out of memory.【多种场景下的解决方案】

RuntimeError: CUDA out of memory.【多种场景下的解决方案】 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;【Matplotlib之旅&#xff1a;零基础精通数据可视化】 &#x1f3c6;&#x1f3c6;关注博主&#xff0c;随时获取更多关于深度学…

VUE2整合markdown编辑器 mavon-editor

GITEE文档 文档中详细介绍了自定义工具栏等 toolbars: {bold: true, // 粗体italic: true, // 斜体header: true, // 标题underline: true, // 下划线strikethrough: true, // 中划线mark: true, // 标记superscript: true, // 上角标subscript: true, // 下角标quote: true, …

行人重识别

&#xfeff;在人的感知系统所获得的信息中&#xff0c;视觉信息大约占到80%&#xff5e;85%。行人重识别&#xff08;person re-identification&#xff09;是近几年智能视频分析领域兴起的一项新技术&#xff0c;属于在复杂视频环境下的图像处理和分析范畴&#xff0c;是许多…

【最优化】一维搜索

首先我们需要先明确一下我们的任务是什么&#xff1f; 我们的任务是给定一个未知函数&#xff0c;如何找到它的最小值。 三点二次插值法 给定三个点&#xff0c;拟合一条二次曲线&#xff0c;每次迭代更新&#xff0c;当时停止迭代。 GitHub - ldx-star/Numerical-Optimizati…

Redis(十四)双写一致性工程案例

文章目录 问题概述canal功能安装部署mysql配置canal服务端canal客户端&#xff08;Java程序&#xff09; 问题概述 canal https://github.com/alibaba/canal 功能 数据库镜像数据库实时备份索引构建和实时维护(拆分异构索引、倒排索引等)业务 cache 刷新带业务逻辑的增量数据…