Linux——进程信号(二)
引言
在进程信号(一)中我们已经讲到了信号的保存,那么接下来要讲信号的处理了。
信号的处理主要要回答3个问题:
1.信号什么时候被处理的?
2.信号如何被处理的?
3.捕捉信号还有其他方式吗?
首先回答问题一:信号在合适的时候被处理,那什么是合适的时候呢?
这就要先看什么是内核态和用户态了
一、内核态和用户态
在这里先回答到底是什么时候:
进程从内核态切换回用户态的时候(CPU中有寄存器来专门标识执行状态),信号会被检测并处理
OS是一个进行软硬件资源管理的软件,它很容易就能获取到CPU中CR3寄存器中是0还是3,从而知道当前是用户态还是内核态
1.1内核态和用户态的概念
用户态:一种用来执行普通用户代码的状态,是一种受监管的状态
内核态:通常用来执行OS的代码,是一种权限非常高的状态
执行系统调用时,是由操作系统来执行的,而不是用户。那么有引出了一个问题:一个进程又是怎么跑到OS中执行代码的呢?
之前我们说地址空间[0,3]GB的用户空间,[3,4]GB是内核空间
用户所写的代码和数据位于用户空间为了保证进程的独立性,每个进程都有自己的进程地址空间,都有一个用户级页表
还有一份所有进程共用的内核级页表
进程地址空间的 3~4GB是不允许用户访问的,因为这1GB空间的代码和数据通过内核级页表和内存中的OS相映射。内存中只存在一份内核,所以所有进程的虚拟地址空间的那1G内核空间都通过同一份内核级页表映射到同一个内核
所以回答一下刚才的问题:进程又是怎么跑到OS中执行代码的呢?
1.进程使用系统调用或者访问系统数据,其实还是在进程的地址空间内进行跳转的
2.进程无论如何切换总能找到操作系统,我们访问操作系统本质就是通过进程的地址空间的[3,4]GB来访问
3.不同的进程指向同一张内核级页表,这样就都能进行系统调用
1.2内核态和用户态转化
用过系统调用->陷入内核->OS执行系统调用->结果给用户
在处理信号的过程(捕捉) 中,一共会有4次状态切换(内核/用户态)
- 当执行到用户代码中的系统调用函数时,会跳转到虚拟地址空间中的内核空间去执行对应的方法,此时陷入内核,状态从用户态变成内核态
- 当系统系统调用被执行完后,再返回之前,OS会检查task_struct中的两张位图表,然后根据handler表中的处理方式去处理相应的信号,如果此时是自定义的处理方式,那么OS会用这handler表中此自定义函数的地址去执行用户自定义的处理方式,此时状态从内核态转化为用户态
- 在执行完信号处理函数后,返回时会执行特殊的系统调用,再次进入内核,此时状态从用户态变成内核态
- 特殊系统调用执行完返回用户模式,返回用户代码原来的位置继续向下执行,此时状态从内核态转化为用户态
为什么我们在信号捕捉的时候,执行我们写的方法(自定义),还要从内核态切换到用户态?(用户态执行方法)
OS不信任(用户),还是让用户去(在外面)用系统调用安全些(万一你的方法时一些越权的非法操作呢,操作系统还要继续执行吗)
二、处理信号sigaction
信号的捕捉除了前面用过的signal函数之外,我们还可以使用sigation函数对信号进行捕捉
man sigaction
sigaction函数可以读取和修改与指定信号相关联的处理动作,该函数调用成功返回0,出错返回-1
signum:指定信号的编号
若act指针非空,根据act修改该信号的处理动作
若oldact指针非空,则通过oldact传出该信号原来的处理动作
act和oact指向sigaction结构体:
struct sigaction
{void(*sa_handler)(int);void(*sa_sigaction)(int,siginfo_t *,void *);sigset_t sa_mask;int sa_flags;void(*sa_restorer)(void);
};
对于sigaction结构体第一个成员来说:
将sa_handler赋值为
SIG_IGN传给sigaction函数,表示忽略此信号
SIG_DFL,表示执行系统默认动作
一个函数指针,表示用自定义函数捕捉信号,或者说向内核注册了一个信号处理函数
第二个成员是实时信号的处理函数,我们不讲
第三个:sa_mask
当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么 它会被阻塞到当前处理结束为止。 如果在调用信号处理函数时,除了当前信号被自动屏蔽之外,还希望自动屏蔽另外一些信号,则用sa_mask字段说明这些需要额外屏蔽的信号,当信号处理函数返回时自动恢复原来的信号屏蔽字。
第四、五个不讲
sigaction的使用:
将2号信号自定义捕捉,打印信号编号,设置sa_mask当2号递达后会被阻塞
#include<iostream>
#include<unistd.h>
#include<signal.h>
using namespace std;void Print(sigset_t &pending)
{cout<<"Pending bitmap: ";for (int signub = 31; signub > 0; signub--){if (sigismember(&pending, signub)){std::cout << "1";}else{std::cout << "0";}}std::cout << std::endl;
}void sigcb(int signo)
{cout<<"signal:"<<signo<<endl;sigset_t pending;sigemptyset(&pending);while(1){sigpending(&pending);Print(pending);sleep(1);}
}
int main()
{struct sigaction act,oact;act.sa_handler = sigcb;act.sa_flags = 0;sigemptyset(&act.sa_mask);sigaction(2,&act,&oact);while(true){sleep(1);} return 0;
}
三、可重入函数
可重入函数:被不同执行流重复进入不会产生问题的函数
以链表的插入为例,当main函数(主执行流)的insert插入执行一半时,又来一个新信号自定义处理方法中又调用了insert函数->该函数被重复进入了(main执行流、信号捕捉执行流)->导致产生了问题:该函数叫做不可重入函数(我们用到的大部分函数都是不可重入的)
像上例这样,insert函数被不同的控制流程调用,有可能在第一次调用还没返回时就再次进入该函数,这称为重入,insert函数访问一个全局链表,有可能因为重入而造成错乱,像这样的函数称为 不可重入函数,反之,如果一个函数只访问自己的局部变量或参数,则称为可重入(Reentrant) 函数。
如果一个函数符合以下条件之一则是不可重入的:
调用了malloc或free,因为malloc也是用全局链表来管理堆的。
调用了标准I/O库函数。标准I/O库的很多实现都以不可重入的方式使用全局数据结构
volatile关键字
volatile是C语言中的一个关键字,它的作用是保持内存的可见性
#include <stdio.h>
#include <signal.h>
int flag = 0;
void handler(int signo)
{cout<<"change flag:"<<flag;flag = 1;cout<<"->"<<flag<<endl;
}
int main()
{signal(2, handler);while(!flag);printf("process quit normal\n");return 0;
}
在接收到2号信号后,quit从0变成1,main函数正常结束,不在循环
编译器又是会做很多优化,g++编译器可以指定优化级别 -O3是最高的优化级别
mytest:sigaction.ccg++ -o $@ $^ -std=c++11 -O3
.PHONY:clean
clean:rm -f mytest
这时重新编译
发送2好信号后进程没有退出。flag改为1,但是主执行流还在循环
当flag添加volatile关键字后
#include <stdio.h>
#include <signal.h>
volatile int flag = 0;
void handler(int signo)
{cout<<"change flag:"<<flag;flag = 1;cout<<"->"<<flag<<endl;
}
int main()
{signal(2, handler);while(!flag);printf("process quit normal\n");return 0;
}
进程正常结束了
SIGCHLD信号
进程一章讲过用wait和waitpid函数清理僵尸进程,父进程可以阻塞等待子进程结束,也可以非阻 塞地查询是否有子进程结束等待清理(也就是轮询的方式)。采用第一种方式,父进程阻塞了就不 能处理自己的工作了;采用第二种方式,父进程在处理自己的工作的同时还要记得时不时地轮询一下,程序实现复杂。
其实,子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略,父进程可以自 定义SIGCHLD信号的处理函数,这样父进程只需专心处理自己的工作,不必关心子进程了,子进程 终止时会通知父进程,父进程在信号处理函数中调用wait清理子进程即可。
请编写一个程序完成以下功能:父进程fork出子进程,子进程调用exit(2)终止,父进程自定 义SIGCHLD信号的处理函数,在其中调用wait获得子进程的退出状态并打印。
事实上,由于UNIX 的历史原因,要想不产生僵尸进程还有另外一种办法:父进程调 用sigaction将SIGCHLD的处理动作置为SIG_IGN,这样fork出来的子进程在终止时会自动清理掉,不 会产生僵尸进程,也不会通知父进程。系统默认的忽略动作和用户用sigaction函数自定义的忽略 通常是没有区别的,但这是一个特例。此方法对于Linux可用,但不保证在其它UNIX系统上都可 用。请编写程序验证这样做不会产生僵尸进程。
相关文章:

Linux——进程信号(二)
引言 在进程信号(一)中我们已经讲到了信号的保存,那么接下来要讲信号的处理了。 信号的处理主要要回答3个问题: 1.信号什么时候被处理的? 2.信号如何被处理的? 3.捕捉信号还有其他方式吗? 首先回答问题一࿱…...

2024.5组队学习——MetaGPT(0.8.1)智能体理论与实战(下):多智能体开发
传送门: 《2024.5组队学习——MetaGPT(0.8.1)智能体理论与实战(上):MetaGPT安装、单智能体开发》《2024.5组队学习——MetaGPT(0.8.1)智能体理论与实战(中)&…...

SQL开窗函数
文章目录 概念:语法:常用的窗口函数及示例:求平均值:AVG() :求和:SUM():求排名:移动平均计数COUNT():求最大MXA()/小MIN()值求分区内的最大/最小值求当前行的前/后一个值 概念: 开窗…...

[xx点评完结]——白马点评完整代码+rabbitmq实现异步下单+资料,免费
项目所有功能已测,均可以跑通,Jmeter和RabbitMQ也都测了。 项目源码:dianpinghui: 仿黑马点评项目 资料: https://pan.baidu.com/s/1kTCn9PxgeIey90WgM4KRqA?pwdn66b 对佬有帮助可以给个star哈,感谢🌹🌹dz…...

Hadoop+Spark大数据技术 实验8 Spark SQL结构化
9.2 创建DataFrame对象的方式 val dfUsers spark.read.load("/usr/local/spark/examples/src/main/resources/users.parquet") dfUsers: org.apache.spark.sql.DataFrame [name: string, favorite_color: string ... 1 more field] dfUsers.show() -----------…...
认知V2X的技术列一个学习大纲
为了深入学习和理解V2X(Vehicle to Everything)技术,以下是一个学习大纲的概述,结合了参考文章中的相关数字和信息: 一、V2X技术基础 V2X概述 定义:V2X是车用无线通信技术,将车辆与一切事物相连…...

揭秘齿轮加工工艺的选用原则:精准打造高效传动的秘密武器
在机械制造领域,齿轮作为传动系统中的重要组成部分,其加工工艺的选择至关重要。不同的齿轮加工工艺会影响齿轮的精度、耐用性和效率。本文将通过递进式结构,深入探讨齿轮加工工艺的选用原则,带您了解如何精准打造高效传动的秘密武…...

Linux-应用编程学习笔记(二、文件I/O、标准I/O)
一、文件I/O基础 文件 I/O 指的是对文件的输入/输出操作,就是对文件的读写操作。Linux 下一切皆文件。 1.1 文件描述符 在 open函数执行成功的情况下, 会返回一个非负整数, 该返回值就是一个文件描述符(file descriptor&#x…...

AI爆文写作:根据别人的爆款标题,如何通过名词替换改成自己的爆款标题?
在日常刷到爆文的时候,就可以培养自己的网感,为啥这篇文章会爆? 这篇爆文的标题有啥诀窍呢? 比如下面这一篇:《极简生活:变富就是每天循环5个动作》 我们可以发现,每天循环5个动作 这几个词语…...

Mybatis源码剖析---第二讲
Mybatis源码剖析—第二讲 那我们在讲完了mappedstatement这个类,它的一个核心作用之后呢?那下面我有一个问题想问问各位。作为mappedstatement来讲,它封装的是一个select标签或者insert标签。但是呢,我们需要大家注意的是什么&am…...

SpringMvc-restful设计风格
Restful 1、入门1.1 简介1.2 实例 1、入门 1.1 简介 RESTFul是什么 RESTFul是WEB服务接口的一种设计风格。 RESTFul定义了一组约束条件和规范,可以让WEB服务接口更加简洁、易于理解、易于扩展、安全可靠。 1.2 实例 web.xml <?xml version"1.0"…...

在未来你将何去何从?
在数字化的浪潮中,信息技术行业无疑是推动全球经济和社会发展的重要动力。随着科技的不断迭代与进步,云计算、大数据、人工智能(AI)、物联网(IoT)、5G通信和区块链等技术已经深入到我们生活的每一个角落&am…...
Vue.js组件设计模式:构建可复用组件库
在Vue.js中,构建可复用的组件库是提高代码复用性和维护性的关键。下面是一些设计模式,说明如何创建可复用的Vue组件: 1. 单文件组件(Single File Component, SFC) Vue.js组件通常是单文件组件,包含HTML、…...

【C语言】指针运算
前言 前面在“走进指针世界”中我已经讲解过指针相关的很多前置知识,其实还有一个很重要的部分就是指针的运算。这篇博客,就让我们一起了解一下指针的运算吧! 指针作为变量,是可以进行算术运算的,只不过情况会和整型…...
Python学习(3) 函数
定义 定义一个函数的格式: def 函数名(参数):执行代码如果没有参数,则称为无参函数。 定义时小括号中写的是形参(形式参数),调用时写的是实参(实际参数)。 调用 调用格式: def…...

计算机网络安全控制技术
1.防火墙技术 防火墙技术是近年来维护网络安全最重要的手段,但是防火墙不是万能的,需要配合其他安全措施来协同 2.加密技术 目前加密技术主要有两大类:对称加密和非对称加密 3.用户识别技术 核心是识别网络者是否是属于系统的合法用户 …...

WordPress插件Disable WP REST API,可根据是否登录来禁用REST API
前面跟大家分享了代码版禁用WordPress REST API的方法(详见『WordPress4.7以上版本如何禁用JSON REST API?』),不过有些站长不太敢折腾自己的网站代码,那么建议试试这款Disable WP REST API,它可以…...

腾讯云COS上传文件出现的问题
1、没有配置 ObjectMetadata 的文件长度 腾讯云COS上传文件出现数据损坏问题_no content length specified for stream data. strea-CSDN博客 2、 使用 FileInputStream使用完没有及时关闭导致报错 ClientAbortException: java.nio.channels.ClosedChannelException 添加…...

【C++】<知识点> 标准和文件的输入输出
目录 一、输入输出操作 1. 相关的类 2. 标准流对象 3. istream类的成员函数 二、流操纵算子 1. 整数流的基数 2. 浮点数精度的流操纵算子 3. 域宽的流操纵算子 4. 其他的流操纵算子 5. 用户自定义流操纵算子 三、文件读写 1. 文本文件的读写 2. 二进制文件的读写 3. 文件读写…...
在阿里Anolis OS 8.9龙蜥操作系统安装docker
在Anolis OS 8系统安装docker 1.更新系统 sudo dnf update -y2.安装依赖包 sudo dnf install -y yum-utils device-mapper-persistent-data lvm23.添加Docker的官方仓库 sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo4.安装…...

短剧APP开发,短剧行业发展下的财富密码
今年以来,短剧市场展现出了繁荣发展的态势,成为了一个风口赛道。 短剧具有不拖沓、时长短、剧情紧凑等优势,顺应了当代人的生活,是当代人的“电子榨菜”。 短剧的快速发展同时也带动了新业态新模式的发展,短剧APP就是…...
简述分代垃圾回收器是怎么工作的?
分代垃圾回收器是一种用于管理和回收内存中垃圾对象的技术。它根据对象的存活时间将内存分为不同的代,并针对每个代应用不同的垃圾回收策略。 分代垃圾回收器的工作过程如下: 内存分代:首先,将内存分为不同的代,通常是…...
Qt 自定义代理类
一.使用步骤 继承QStyledItemDelegate类:首先创建一个新的类并继承自QStyledItemDelegate类,作为您的自定义代理类。 实现代理类的构造函数:在代理类中实现构造函数,并在构造函数中调用基类的构造函数,可以选择传入一…...
android GridLayout 布局详解,并举例
GridLayout 是 Android 中的一个布局容器,它允许你在一个二维网格中排列子视图。你可以指定网格的行数和列数,或者让 GridLayout 自动计算它们。每个子视图都可以占据一个或多个网格单元格。GridLayout 非常适合在需要创建规则网格的应用中使用ÿ…...

el-transfer和el-tree进行结合搞一个树形穿梭框
由于业务需求需要在穿梭框里使用树形结构,但是本身element里并不支持,于是参考了别的大佬发的文章作为思路及后续自己新增了一些处理功能。 目录 1.拷贝代码放到自己的项目目录中 2.改造el-transfer的源码 3.修改tree-transfer-panel.vue文件 4.修改…...

编一个自己的万年历
编一个自己的万年历 前阶段突然想查一下某一天是星期几,于是自己编了一个[小程序][https://blog.csdn.net/weixin_41905135/article/details/138972055?spm1001.2014.3001.5501],但是功能很单一,就是单纯的查是星期几。(虽然用网…...
Golang gin框架中间件c.JSON返回结果后终止返回
gin框架中间件c.JSON返回结果后还是会继续执行之后的方法,我们可以用c.Abort()来终止后续的处理 func MiddlewareFunction(c *gin.Context) {// 假设有某种条件下需要返回错误if someCondition {c.JSON(http.StatusBadRequest, gin.H{"error": "som…...

码蹄集部分题目(2024OJ赛16期;单调栈集训+差分集训)
🧀🧀🧀单调栈集训 🥪单调栈 单调递增栈伪代码: stack<int> st; for(遍历数组) {while(栈不为空&&栈顶元素大于当前元素)//单调递减栈就是把后方判断条件变为小于等于即可{栈顶元素出栈;//同时进行其他…...

安卓玩机搞机技巧综合资源----自己手机制作证件照的几种方法 免费制作证件照
接上篇 安卓玩机搞机技巧综合资源------如何提取手机分区 小米机型代码分享等等 【一】 安卓玩机搞机技巧综合资源------开机英文提示解决dm-verity corruption your device is corrupt. 设备内部报错 AB分区等等【二】 安卓玩机搞机技巧综合资源------EROFS分区格式 小米红…...

揭秘循环购模式:消费返利新玩法,引领电商新潮流
在当今的消费市场中,有一种商业模式引起了广大消费者的热烈讨论——那就是循环购模式。你可能会想,消费满千元就能得到两千元的福利,每天还能领取现金,这怎么可能呢?商家难道真的在“慷慨解囊”?今天&#…...