进程信号的保存和处理

news2024/11/29 0:36:46

目录

🏆一、信号的保存

①信号的捕捉

 ②sigset_t

 ③sigaction

🏆二、不可重入函数

🏆三、volatile

 🏆四、SIGCHLD

🏆一、信号的保存

在聊信号保存之前,我们不妨想一个问题,如果把所有信号都自定义设置行为,是否进程就无法杀死了呢?

 

 

 为了避免这种情况出现,OS中kill -9 可以强制杀死进程,也就是说无法对9号信号进行自定义动作的!

信号的几个专业术语:

1、实际执行信号的处理动作称为信号递达(Delivery)

2、信号从产生到递达之间的状态,称为信号未决(Pending)

3、进程可以选择阻塞(Block)某个信号,被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。阻塞和忽略是不同的,只要不被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。

那么信号在进程pcb中是如何存储的呢?

🖊 在task_struct结构中,有pending位图block位图,其中pengding位图表示是否收到了某些信号,而block位图则是表示是否阻塞了那些信号。而handler数组则是一个指针数组,它其中存储的是函数指针,它对应的是每个信号对应的处理方法(默认,忽略,自定义)。

🖊pengding位图每个比特位的位置表示信号编号,而比特位的内容表示是否收到了某些信号(0表示没有收到,1表示收到)。

🖊block位图每个比特位的位置表示信号编号,而比特位的内容表示是否阻塞了某些信号(0表示没有阻塞,1表示阻塞)。

🗡理解阻塞不是阻止

 比如2号信号,如果我们阻塞了它,那么OS发送2号信号给进程是会修改pending位图2号信号对应的比特位由0变为1,但是因为是block,所以不做处理,当解除阻塞时还是会处理的。

信号的未决是一种状态,指的是从信号的产生到被处理前的这一段时间;信号的"阻塞"是一个开关动作,指的是阻止信号被处理,而不是阻止信号产生

①信号的捕捉

信号在产生的时候,不会被立即处理,而是在合适的时候(从内核态返回到用户态的时候,进行处理!)。

这里简单说一下为什么要有内核态用户态,因为我们的task_struct本身是由OS维护的,所以说要对pending位图进行修改,是需要内核去修改的,我们普通用户是没有权限的!

🎄内核态和用户态

细说内核态和用户态

我们平时编译的代码都是用户态的,比如说编写一些代码在编译器上。而我们难免会访问两种资源:

1、操作系统自身的资源(比如getpid(),waitpid()).

2、硬件资源(printf,write,read接口)

用户为了访问内核或者硬件资源,必须通过系统调用来完成访问,那么在调用这些OS接口还有访问硬件接口,就需要从用户态切换到内核态。需要注意的是这种行为本身是影响效率的,多次由用户态转变为内核态,是很花费时间的---尽量避免频繁调用系统调用

这些例子很多了,简单来说STL接口在设计时扩容时1.5倍或者2倍扩容就是为了减少系统调用的频次。

那么怎么知道自己是处于用户态还是内核态呢?

🗡CPU表征状态

还得看CPU,我们知道进程会把自己的上下文信息寄存在CPU中,CPU中有大量的寄存器:画个简图:

其中有一个CR3寄存器用来表征当前进程的运行级别:

0表示内核态,而3表示用户态。

 那么知道表征处于什么状态后,怎么由用户态跳转到内核态呢?

32位系统下,4G的虚拟地址,其中1-3G是用户级,而3-4GB则是内核级,无论进程如何切换都不会更改这一区域。 所以每个进程都可以随意访问OS,只需在虚拟地址上进行跳转即可!

所以我们只需更改CR3寄存器状态由用户态变为内核态,由3变为0,再在虚拟地址上跳转。

这些操作我们用户不需要做,当调用系统接口时,会帮我们由用户态转为内核态,然后才能跳转到OS空间,结束的时候再切换回用户态。

这里这个图表更能表现信号是如何在task_struct中存储的。那么在从内核态回到用户态时要查看block阻塞位图pending位图,依次遍历二进制信号编号,如果block位图上信号编号对应的二进制为1,就不做处理,也就是未决状态如果为0,再查看pending位图,如果为0不做处理,如果为1,调用对应的handler方法(默认,忽略,自定义)

 用户态不能执行内核态代码,因为权限不够,而内核态虽然理论上可以执行用户态代码,实际是不行的:因为OS不相信任何进程,以防出现篡改系统数据等非法行为。所以内核态要经过特定的调用,将自己的身份重新更改为用户态,然后再执行用户态代码!

系统调用-->内核态--->检测信号--->调用捕捉方法--->返回内核态--->返回用户态

画个符号,这个图比较贴切。

 

 ②sigset_t

之前都是纸上谈兵,真正实操层面还得上代码。说到代码就得介绍一批OS提供的接口。

首先我们要介绍信号集操作函数

sigemptyset():初始化一个自定义信号集,将其所有信号都清空,也就是将信号集中的所有的标志位置为0,使得这个集合不包含任何信号,也就是不阻塞任何信号 。

sigfillset ():用来将参数set信号集初始化,然后把所有的信号加入到此信号集里即将所有的信号标志位置为1,屏蔽所有的信号。

sigaddset ()用来将参数signum 代表的信号加入至参数set 信号集里。

sigdelset() 允许您从一个自定义信号集中删除一个指定的信号,也就是将该信号的标准位设为0,不阻塞这个信号。

sigismember ()用来测试参数signum 代表的信号是否已加入至参数set信号集里。 如果信号集里已有该信号则返回1,否则返回0。 如果有错误则返回-1。

要理解上面这些函数,必须要先理解sigset_t类型。

因为每个信号只有一个bit的未决标识,非0即1.不记录该信号产生了多少次,阻塞标识也是这样表示的。因此未决和阻塞标识可以用相同的数据类型sigset_t来存储。sigset_t称为信号集。这个类型可以表示每个信号的"有效"或"无效"状态,在阻塞信号集中"有效"和"无效"的含义是该信号是否被阻塞,而在未决信号集中"有效"和"无效"的含义是该信号是否处于未决状态。

sigpending()

 

这个函数用来检查pending信号集,获取当前进程的pending信号集,通过用户设置的sigset_t类型的set,哪一个进程调用的sigpending,就获得哪一个进程的pending位图

sigprocmask()

 how:SIG_BLOCK:设置阻塞某个信号。SIG_UNBLOCK:取消阻塞某个信号。SIG_SETMASK:阻塞信号集设置为我们设置的set。

set:要设置的信号掩码

oldset:之前设置的信号掩码

 上面这段代码,我们把2号信号阻塞了,需要观察验证的现象是:2号信号被block无法递达,可以看到被pending,但是阻塞不执行。

 再来一段2号信号先被阻塞,然后再被恢复的过程:

 

 通过演示,发现10s后解除对2号信号的阻塞后进程就直接退出了。因为阻塞的2号信号的默认动作是终止进程,当不再阻塞2号信号时,进程就直接退出了,看不到后序打印了。

想看到后序执行用户态代码,需要对2号信号进行自定义捕捉!

 给2号和3号设置自定义动作,然后将2号和3号添加进阻塞。10s后解除阻塞,就看到了自定义动作。

 ③sigaction

sigaction()是不同于signal()的捕捉信号的方法。

对特定信号设置特定的回调方法,当触发信号时,执行对应的捕捉动作。

 

signum参数指出要捕获的信号类型,act参数指定新的信号处理方式,oldact参数输出先前信号的处理方式。

需要重点关注的是sigaction结构体中sa_handler,sa_mask以及sa_flags.

sa_handler就是我们的自定义动作,它是一个回调函数。

sa_mask就是阻塞信号集

sa_flags则是 指定信号处理的行为,这里我们设置为0.

 

 上面只是简单的对于sigaction()函数的使用。

 对于2号信号进行了自定义行为重写。这里其实要引出和解决一个疑问的。先看动图:

 如果我们在捕捉2号信号期间,多次发送2号信号,会发生什么呢?通过动图可以看到,只保留了前两次相同信号。为什么?

1、当我们进行正在递达某一个信号期间,同类型信号无法被递达--当当前信号正在被捕捉,OS会自动将当前信号加入到进程的信号屏蔽字,所以正在处理2号信号时,后序2号信号不会被递达!

2、当信号完成捕捉动作,系统又会自动解除对该信号的屏蔽。

一般一个信号被解除阻塞的时候,如果他被pending,会自动进行递达当前阻塞信号。

 

 

 通过gif可以看到在捕捉2号信号期间,3号被屏蔽,但是我们发送3号信号,会修改pending位图,在结束屏蔽时会捕捉3号信号!

🏆二、不可重入函数

 上面这个图展示了这样一种场景:

上图是链表插入的一种情况,当主函数流调用了插入链表,而这时发送信号,信号捕捉执行自定义动作也是插入链表,那么这时是插入node1,还是插入node2呢?如果插入node1会导致node2丢失,同理插入node2会导致node1丢失,导致内存泄漏。

这种主函数和自定义行为中的函数相同的,称为不可重入函数。

一般而言,我们认为:main执行流和信号捕捉执行流是两个执行流。

如果在main函数中,和在handler函数中,该函数被重复进入,出问题--该函数是不可重入函数。如果在main函数中,和在handler函数中,该函数被重复进入,没有出问题---该函数是可重入函数。

目前大部分情况下的接口,都是不可重入的!这是一种特性而不是缺陷。

🏆三、volatile

🖊优化级别

通过man gcc 往下翻阅,可以查到当前Linux编译器的优化级别。

我们都知道vs上编写的代码有release版本和debug版本,release版本对debug版本进行了优化,那么具体是怎么优化的,优化了之后又和debug有什么区别呢?今天来讨论一下。

一般默认优化级别是-O0。没有 优化

 

 

上面是没有优化的。我们看到正如我们所料,当调用自定义行为时,将quit修改,循环终止进程退出。那么我们加上优化呢?

在编译时加上-O3优化级别

 

 通过演示,可以看到,调整优化级别后,为什么不会退出了呢?

注意,这里只是为了方便演示问题,所以加上-O3优化,一般不建议。

还得回归到硬件CPU的角度来解答。

一般级别:

当while循环执行的时候,CPU不断从物理内存读取quit值到寄存器做判断。但是我们的main执行流和handler执行流是两个执行流。当我们一般优化的时候,默认从物理内存取到quit到CPU中判断。当quit被改为1的时候,再从物理内存中读取,quit为1,不满足循环条件,循环终止。

优化:

代码本身没有问题,但是因为优化策略导致出现问题。为了解决这个问题,引入了volatile关键字。这个关键字是为了保持内存可见性。

 

 

 🏆四、SIGCHLD

子进程在死亡或停止的时候,会向父进程直接发送sigchld信号来告诉自身的死亡或者停止。

怎么验证呢?还是用到自定义行为!

 

 验证子进程退出时确实向父进程发送了SIGCHLD信号。我们不再需要轮询子进程,而是子进程退出时告诉了父进程!

基于这个特性,那么要想不产生僵尸进程,还有一个办法:

父进程调用sigactionSIGCHLD的处理动作置为SIG_IGN,这样fork出来的子进程在终止时会自动清理掉,不会产生僵尸进程,也不会通知父进程。

 

 我们看到不需要父进程等待,子进程自动被OS回收。但是需要注意这种方式在Linux下是有效的,其他的操作系统就不一定了。

还有最后一个问题 

既然默认是忽略,还进行设置是否多此一举呢?

并不是,默认设置忽略和手动设置忽略动作是不一样的。当我们使用默认的忽略动作就是之前的父进程需要等待子进程回收,而手动设置的忽略,OS会自动回收子进程!

 

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

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

相关文章

易岸教育:高端篇!公务员面试经验总结!

根据众多应聘者的面试经验,总结出以下几点供应聘者参考: 1、注意着装。 适当提高服装档次。 衣着得体大方,可以提升形象,增强自信。 2、安心。 要以一颗平常心正确对待面试,就要有受挫的心理准备。 即使面试失败了一…

【Python】学习导论:Python 简介

前言:XMind 脑图大纲 一、Python 的发展和演变 1.1 Python 1.0(1994年) 版本:Python 的第 1 个正式版本典型里程碑:包含了基本的语言特性和标准库 1.2 Python 2.0(2000年) 版本:Pyt…

XDP入门--通过用户态程序自动加载与卸载eBPF程序字节码到网卡

本文目录 1、测试环境1、eBPF字节码的源代码实现3、用户态应用程度实现简介4、编译与运行5、运行状态验证 通过文章XDP入门–之hello world 我们知道,可以通过iproute2的ip工具向网卡去加载和卸载eBPF程序的字节码。但这个使用起来不太方便。而且在需要网卡恢复正常…

浅析Java 多线程中的锁

前言 随着互联网技术的快速发展,多线程编程已经成为了现今编程领域中必不可少的知识点之一。Java 是一种广泛使用的编程语言,也是一些底层应用程序和高并发应用程序的首选语言。而 Java 提供的多线程编程机制和相关的锁机制,则成为了 Java 开…

SpringBoot 上传图片-指定目录按照日期存储

SpringBoot 上传图片-指定目录按照日期存储 1. 在配置文件中指定文件保存根目录 我用的yaml,用properties也行 file-save-path: D:/upload/2. 文件上传接口 package com.admin.controller.wechat;import cn.hutool.core.lang.UUID; import com.redic.base.Result; import com…

SpringMVC 程序开发

✏️作者:银河罐头 📋系列专栏:JavaEE 🌲“种一棵树最好的时间是十年前,其次是现在” 目录 什么是 Spring MVCMVC 定义 怎么学 Spring MVCSpring MVC 创建和连接创建 Spring MVC 项目RequestMapping 注解介绍PostMappi…

二叉搜索树的实现(C语言)

目录 前言: 一:准备工作 (1)需要的头文件 (2)树节点结构体描述 (3)初始化 二:指针 三:插入新节点(建树) (1)生成一个新节点 (2)找插入位置 四:查找和遍历 (1)查找 (2)遍历 五:删除节点 六&…

mysql 主从同步

① 修改 master 配置文件② 新建同步账号③ 创建数据库④ 修改 slave 配置文件⑤ 配置主从关系⑥ 检验主从结果 角色ipmaster192.168.233.100slave1192.168.233.101slave2192.168.233.102 禁用 selinux 跟 firewal l情况下: ① 修改 master 配置文件 vim /etc/my…

NEFU linux实验二

在linux中,家目录又称“home目录”、“主目录”,是用户的宿主目录,通常用来保存用户的文件,可以使用“~”来表示。一个用户登录系统,进入后所处的位置就是“/home”,即家目录;root用户的家目录为…

路径规划算法:基于鸡群优化的路径规划算法- 附代码

路径规划算法:基于鸡群优化的路径规划算法- 附代码 文章目录 路径规划算法:基于鸡群优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要:本文主要介绍利用智能优化算法鸡群…

输电线路故障诊断(利用随机森林方法实现二分类和多分类)

1.simunlike仿真系统的建立( 运行效果 :输电线路故障诊断_哔哩哔哩_bilibili) 2.在仿真系统的基础上获取数据集 分别获取单相接地故障、两相接地故障、两相间短路故障、三相接地故障、三相间短路故障和正常状态下的电流(Ia,Ib,I…

一文带你了解MySQL之optimizer trace神器的功效

前言: 对于MySQL 5.6以及之前的版本来说,查询优化器就像是一个黑盒子一样,你只能通过EXPLAIN语句查看到最后优化器决定使用的执行计划,却无法知道它为什么做这个决策。这对于一部分喜欢刨根问底的小伙伴来说简直是灾难&#xff1…

2023年5月14日蓝桥杯c++省赛中级组

选择题讲解 1.)C++中,bool类型的变量占用字节数为 ( )。 A.1B.2 C.3 D.4 答案:A 解析:(C++ 中 bool 类型与 char 类型一样,都需要1 byte。一些其他类型的占用字节数:short:2 byte。int:4 byte。long long:8 byte。double:8 byte。) 2.)以下关于C++结构体的说…

代码随想录二刷 day04 | 链表之 24两两交换链表中的节点 19删除链表的倒数第N个节点 面试题 02.07. 链表相交 142.环形链表II

24. 两两交换链表中的节点 题目链接 解题思路: 先将一些可能会改变的节点保存一下,然后再按照三个步骤就行修改 注意 要使用改变以后节点的指针(这个地方一刷的时候没注意到,稀里糊涂的过去了) 代码如下:…

【八股】计算机网络-HTTP和HTTPS的区别、HTTPS加密传输原理

计算机网络-HTTP和HTTPS的区别、HTTPS加密传输原理 一、HTTP和HTTPS的基本概念二、HTTP与HTTPS的区别三、HTTPS加密传输原理1. 什么是HTTPS1.1 https诞生的原因1.2 https加密方式1.3.http和https的区别 2. https的工作流程3. 数字证书3.1 什么是数字证书3.2 如何申请数字证书3.…

亚马逊六页纸沟通法,拒绝PPT

亚马逊六页纸沟通管理法,拒绝PPT 使用一种简洁的「结构化备忘录」 内部管理会议沟通,每次不超过六页 趣讲大白话:让沟通更有效 【趣讲信息科技178期】 **************************** 那么“6页备忘录”到底是什么呢? 1. What we d…

供应链 | 在线平台的研究与思考(一):销售渠道与模式选择

封面图来源: https://www.pexels.com/zh-cn/photo/4968391/ 编者按 当前,电商平台主要采用两种销售模式:代理和分销。商家根据自身情况选择线上或线下渠道,而电商平台会根据不同的线上商家选择适当的分销模式。本期编者精选的两…

Unity 工具 之 Azure 微软语音合成普通方式和流式获取音频数据的简单整理

Unity 工具 之 Azure 微软语音合成普通方式和流式获取音频数据的简单整理 目录 Unity 工具 之 Azure 微软语音合成普通方式和流式获取音频数据的简单整理 一、简单介绍 二、实现原理 三、注意实现 四、实现步骤 六、关键脚本 一、简单介绍 Unity 工具类,自己整…

从事黑客工作十余年,究竟如何成为一名高级的安全工程师?

目录 1. 前言 2. 经验 3. 要考虑的问题 4. 学习路线详解 第一步:计算机基础 第二步:编程能力 第三步:安全初体验 第四步:分方向 尾言 参考书籍列表 1. 前言 说实话,一直到现在,我都认为绝大多数…

STP协议

目录 STP的基本概念: 桥ID(Bridge ID): 根桥: 开销(Cost): RPC(根路径开销): Port ID: BPDU:(网桥协议…