【Linux】进程信号(下)
目录
一、信号的阻塞
1.1 信号在内核中的保存方式
1.2 sigset_t信号集
(1)信号集操作
(2)sigprocmask函数
(3)sigpending函数
二、信号的处理
2.1 用户态和内核态
2.2 重谈进程地址空间
三、信号的捕捉
sigaction函数
前文:
【Linux】进程信号(上)-CSDN博客文章浏览阅读729次,点赞29次,收藏27次。本篇文章围绕Linux中的进程信号展开讲解,包含信号的概念、信号的产生等内容https://blog.csdn.net/Eristic0618/article/details/142900955
一、信号的阻塞
1.1 信号在内核中的保存方式
前面提到,进程在接收到普通信号时,并不是立即进行处理的
- 信号产生但未被处理的状态称为信号未决(Pending)
- 执行信号的处理动作,称为信号递达(Delivery)
- 信号产生但可以被阻塞(Block)
假设一个信号被阻塞,则这个信号将一直保持未决的状态,直到进程解除对该信号的阻塞才能执行递达动作
进程的阻塞和信号的阻塞是两回事,信号阻塞类似于屏蔽了某个信号
信号的阻塞与忽略也是不同的,信号被阻塞后会一直存在,而忽略是对信号的一种处理动作
进程的PCB中有三个表,分别是block表、pending表和handler表,其中:
- block表是一个位图,又叫做阻塞信号集或信号屏蔽字,记录了某个信号是否被屏蔽,如果对应位置为0表示该信号未被屏蔽,为1则被屏蔽
- pending表也是位图,用于记录进程收到了哪些信号
- handler表是一个函数指针数组,用于记录每种信号的处理方法
三个表一起看,就能知道一个信号是否被屏蔽,是否被收到,处理方法是什么
我们通过signal函数捕捉某个信号,实际上就是把用户提供的函数指针覆盖到handler表的对应位置
信号的处理方式类型为__sighandler_t,实际定义为:
typedef void(*__sighandler_t)(int);
系统中还定义了一些信号的默认处理方法:
#define SIG_DFL ((__sighandler_t) 0)
#define SIG_IGN ((__sighandler_t) 1)
其中SIG_DFL表示收到信号后终止进程,SIG_IGN表示忽略某个信号
1.2 sigset_t信号集
(1)信号集操作
sigset_t是一个数据类型,称为信号集,本质上是一个位图
我们的block表和pending表的类型都是sigset_t,其每一位对应一个信号,值为1或0则表示是否未决或被阻塞
要对一个信号集进行修改,只能通过特定函数:
#include <signal.h>int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
int sigismember(const sigset_t *set, int signum);
其中:
- sigemptyset函数会将set指向的信号集所有位清零,表示该信号集不包含任何有效信号
- sigfillset函数则会将set指向的信号集每一位置为1
- sigaddset函数会将set指向的信号集中signum号信号对应的位修改为1,表示添加某个信号
- sigdelset函数会将set指向的信号集中signum号信号对应的位修改为0,表示删除某个信号
- sigismember函数会判断signum号信号在set指向的信号集中是否有效,有效返回1,否则返回0,出错返回-1
通过这几个函数,我们就能对一个信号集进行初始化或修改等操作了
(2)sigprocmask函数
#include <signal.h>int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
sigprocmask函数可以读取或修改进程的信号屏蔽字(阻塞信号集),成功返回0,出错返回-1
其中:
- how:更改信号屏蔽字的方式,SIG_BLOCK表示将set中的有效信号添加到当前信号屏蔽字,SIG_UNBLOCK表示将set中的有效信号从当前信号屏蔽字中删除,SIG_SETMASK表示将当前信号屏蔽字设置为set
- set:若为非空,则按照set的内容和how指定的方式对当前信号屏蔽字进行修改
- oset:若为非空,则将当前信号屏蔽字通过该参数读出
若set和oset都为非空,则先读取再修改
(3)sigpending函数
#include <signal.h>int sigpending(sigset_t *set);
sigpending函数可以通过set参数读取当前进程的未决信号集,成功返回0,出错返回-1
例如:
#include <iostream>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>using namespace std;void PrintSigset(sigset_t *set) //打印信号集
{for (int i = 1;i < 32; i++) //遍历所有普通信号{if(sigismember(set, i)) //判断当前信号在信号集中是否有效cout << 1;elsecout << 0;}cout << endl;
}int main()
{sigset_t set, p;sigemptyset(&set);sigaddset(&set, 1); //向信号集中添加1号信号sigprocmask(SIG_BLOCK, &set, NULL); //将set中的有效信号添加到信号屏蔽字中while(true){cout << "process running...,pid: " << getpid() << ", pending: ";sigpending(&p); //取出未决信号集PrintSigset(&p); //打印未决信号集sleep(1);}return 0;
}
运行结果:
在代码中,我们将1号信号添加到了进程的信号屏蔽字中,所以如果进程接收到1号信号后不会对其进行处理
通过运行结果可以发现,在我们向进程发送1号信号后,未决信号集的第一位由0变为1,说明此时1号信号处于未决状态,没有被处理。如果过段时间后再对1号信号解除阻塞,那么进程就会在合适的时候处理信号
当然,和信号的捕捉一样,9和19号信号也是无法被阻塞的。试想一下,如果一个进程将所有的信号都阻塞了,那是不是变得刀枪不入了?
二、信号的处理
2.1 用户态和内核态
我们一直说进程会在合适的时候处理信号,具体是什么时候呢?
进程要知道自己是否收到了信号或是否需要对信号进行处理,就需要访问自己的pending表、block表和handler表。但这三张表都是内核中的数据结构,说明进程一定要处在一种内核状态才能对信号进行处理
首先搞清楚什么是内核态,和与其对应的用户态:
- 用户态(User Mode)是进程运行用户程序的状态,权限较低,只能通过系统调用来请求操作系统提供的服务
- 内核态(Kernel Mode)是操作系统内核的运行状态。进程在调用了系统调用、触发时钟中断或异常后会将CPU的控制权交给操作系统内核并陷入内核态,处于内核态的CPU拥有最高的权限,可以访问操作系统的所有资源
- 为了操作系统的安全,操作系统不允许用户直接对其内部进行操作。但有时用户需要操作系统帮助完成需要更高权限的工作,因此区分了内核态与用户态
这里直接给出结论:我们的进程在从内核态返回到用户态的时候,进行信号的检测与处理
关于进程在用户态和内核态之间的切换,还有一些内容需要我们了解
例如进程在执行一些封装了系统调用的库函数时,就需要从用户态陷入到内核态,其中是通过一条汇编指令 "int 0x80" 实现的
2.2 重谈进程地址空间
Linux的进程地址空间大小为4GB,其中位于较高地址的1GB为内核空间,通过内核级页表映射了操作系统的代码和数据;较低地址的3GB为用户空间,供进程使用,通过用户级页表映射到物理内存中进程自己的代码和数据。
操作系统中有几个进程就有几个用户级页表,但内核级页表在操作系统中只有一份,可以被所有进程看到,所以每个进程的内核空间映射的内容都是一样的
既然内核空间是进程地址空间的一部分,而内核空间又映射了操作系统的代码和数据,那么我们在调用系统调用接口的时候,难道不就是在进程自己的地址空间中调用吗?
站在进程的视角,调用系统调用接口,就相当于我们从用户空间的正文代码中跳转到内核空间,执行完毕后再返回到用户空间
但是操作系统肯定不会允许我们进程随心所欲的访问内核空间的内容,只有进程处于内核态时才拥有访问内核空间的权限。如何判断一个进程是处于用户态还是内核态呢?
- CPU当中存在一个ecs寄存器,若其最低两位比特位为00则代表进程处于内核态,为11则代表进程处于用户态,当执行了int 0x80时就会将这两位从11(用户态)变为00(内核态)。通过检测这两位就可以判断进程处于什么态,是否有权限访问内核空间
既然内核态的权限那么大,那处于内核态的进程能否执行用户空间的代码呢?不行!
依旧是为了操作系统的安全,OS不会执行任何用户所写的代码(避免用户的代码中包含非法操作等情况),因此进程需要从内核态再重新返回用户态执行用户的代码
我们回到信号的话题,看看进程遇到异常收到信号时是如何进行用户态和内核态的切换的
- 当进程发生异常,从用户态陷入到内核态进行异常的处理
- 处理完毕后在返回用户态之前就会检查是否有信号需要处理
- 如果此时对应信号的处理方法是用户自定义的,则需要从内核态返回用户态执行对应函数(如果处理方法是系统默认,则处理完毕后会直接返回代码原来中断的位置继续执行)
- 执行完用户自定义的信号处理方法后,处于用户态的进程无法直接跳转回原来的位置,需要再次陷入内核态
- 处于内核态的进程,能够跳转回主控制流程中上次被中断的地方继续向下执行
三、信号的捕捉
sigaction函数
除了signal函数,我们还可以通过sigaction函数捕捉一个信号(包括实时信号)
#include <signal.h>int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
sigaction函数用于修改特定信号的处理动作,成功返回0,失败返回-1并设置错误码
其中:
- 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);
};
如果要捕捉的是普通信号,我们主要关注结构体中的sa_handler就行,这是我们捕捉信号的自定义处理方法。sa_sigaction是实时信号的处理方法,我们不深入了解
关于sa_mask字段:
在捕捉某个信号时内核会暂时性的将该信号屏蔽,直到将当前信号处理完毕后才会解除屏蔽,避免同样的信号被重复捕捉
如果在处理信号时除了屏蔽当前信号,还希望额外暂时性屏蔽一些其他信号,则通过sa_mask字段进行说明
- 我们自定义的信号处理方法,通过act参数传入,旧的信号处理方法则可以通过oact参数输出
完.
相关文章:

【Linux】进程信号(下)
目录 一、信号的阻塞 1.1 信号在内核中的保存方式 1.2 sigset_t信号集 (1)信号集操作 (2)sigprocmask函数 (3)sigpending函数 二、信号的处理 2.1 用户态和内核态 2.2 重谈进程地址空间 三、信号…...

FlinkCDC 实现 MySQL 数据变更实时同步
文章目录 1、基本介绍2、代码实战2.1、数据源准备2.2、代码实战2.3、数据格式 1、基本介绍 Flink CDC 是 Apache Flink 提供的一个功能强大的组件,用于实时捕获和处理数据库中的数据变更。可以实时地从各种数据库(如MySQL、PostgreSQL、Oracle、MongoDB…...

JavaWeb——Maven(4/8):Maven坐标,idea集成-导入maven项目(两种方式)
目录 Maven坐标 导入Maven项目 第一种方式 第二种方式 Maven坐标 Maven 坐标 是 Maven 当中资源的唯一标识。通过这个坐标,我们就能够唯一定位资源的位置。 Maven 坐标主要用在两个地方。第一个地方:我们可以使用坐标来定义项目。第二个地方&#…...

实现uniapp天地图边界范围覆盖
在uniapp中,难免会遇到使用地图展示的功能,但是百度谷歌这些收费的显然对于大部分开源节流的开发者是不愿意接受的,所以天地图则是最佳选择。 此篇文章,详细的实现地图展示功能,并且可以自定义容器宽高,还可…...
思科网络设备命令
一、交换机巡检命令 接口和流量状态 show interface stats:查看所有接口当前流量。show interface summary:查看所有接口当前状态和流量。show interface status:查看接口状态及可能的错误。show interface | include errors | FastEthernet …...
Egg.js使用ejs快速自动生成resetful风格的CRUD接口
目前的插件能够自动生成egg的crud的都不太好用 我们自己写一个吧 ejs模块 也方便定制 安装依赖 npm install ejs --save ejs 是一个简单易用的模板引擎,常用于 Node.js 应用程序中 在项目根目录下创建 template/controller.ejs 模板文件 use strict;const Co…...

自动化抖音点赞取消脚本批量处理
🌟 前言 欢迎来到我的技术小宇宙!🌌 这里不仅是我记录技术点滴的后花园,也是我分享学习心得和项目经验的乐园。📚 无论你是技术小白还是资深大牛,这里总有一些内容能触动你的好奇心。🔍 &#x…...

基于YOLOv8深度学习的智能车牌检测与识别系统【python源码+Pyqt5界面+数据集+训练代码】目标检测、深度学习实战
背景及意义 智能车牌检测与识别系统通过使用最新的YOLOv8与PaddleOCR算法能够迅速、准确地在多种环境下实现实时车牌的检测和识别。本文基于YOLOv8深度学习框架,通过16770张图片,训练了一个进行车牌检测模型,可以检测蓝牌与绿牌,然后对检测到的车牌使用O…...

qt QGraphicsGridLayout详解
一、概述 QGraphicsGridLayout是Qt框架中用于在QGraphicsScene中布置图形项的一个布局管理器。它类似于QWidget中的QGridLayout,但主要处理的是QGraphicsItem和QGraphicsWidget等图形项。通过合理设置网格位置、伸缩因子和尺寸,可以实现复杂而灵活的布局…...
数字处理系列
(1)将数字转化成中文的过滤器 <template><div><p>数字转中文:{{ 110 | numberToChinese }}</p></div></template><script>export default {filters: {numberToChinese(num) {const chineseNums …...

基于开源Jetlinks物联网平台协议包-MQTT自定义主题数据的编解码
目录 前言 1.下载官方协议包 2.解压 3.自定义主题 4.重写解码方法 5.以下是我解析后接收到的数据 前言 最近这段时间,一直在用开源的Jetlinks物联网平台在学习,偶尔有一次机会接触到物联网设备对接,在协议对接的时候,遇到了…...

【Python】Python2.7升级Python3
需求背景 服务是跑在docker的容器里的,因此要新建image依赖环境是Ubuntu,老的是16.4。 步骤 先准备环境,因为只有你的环境上去了,运行代码的时候才会报错,这样才会把需要改的代码暴露出来。 python3.5目前也是被遗弃的…...
Python 内置函数 round() 详解
在 Python 编程中,round() 函数是一个非常实用的内置函数,用于对数字进行四舍五入。无论是在数据处理、财务计算还是科学计算中,round() 函数都能帮助我们得到所需的精确值。本文将详细介绍 round() 函数的用法和注意事项。 1. round() 函数…...

JavaScript入门中-流程控制语句
本文转载自:https://fangcaicoding.cn/article/52 大家好!我是方才,目前是8人后端研发团队的负责人,拥有6年后端经验&3年团队管理经验,截止目前面试过近200位候选人,主导过单表上10亿、累计上100亿数据…...

kconfig语法(一)
一、安装Kconfiglib python -m pip install windows-curses python -m pip install kconfiglib二、使用样例 ①创建kconfig文件。 ②在kconfig文件添加内容: config KCONFIG_DEMO_ITEM1boolprompt "demonstate item1 for bool learning"config KCONFIG_DEMO_ITE…...
十七、行为型(命令模式)
命令模式(Command Pattern) 概念 命令模式是一种行为型设计模式,它将请求封装成一个对象,从而使您可以使用不同的请求对客户进行参数化,排队请求,以及支持可撤销操作。通过这种模式,调用操作的…...

原材料供应商的GRS认证证书过期了怎么办?
在全球纺织和时尚产业中,GRS(Global Recycle Standard,全球再生标准)认证已成为衡量企业环保和可持续发展的重要指标。然而,当原材料供应商的GRS认证证书过期时,企业需迅速采取行动,以确保供应链…...
C++编程:实现一个基于原始指针的环形缓冲区(RingBuffer)缓存串口数据
文章目录 0. 引言1. 使用示例2. 流程图2.1 追加数据流程2.2 获取空闲块流程2.3 处理特殊字符流程2.4 释放块流程2.5 获取下一个使用块流程 3. 代码详解3.1 Block 结构体3.2 RingBuffer 类3.3 主要方法解析append 方法currentUsed 和 currentUsing 方法release 方法nextUsed 方法…...

LangChain 创始人万字科普:手把手教你设计 Agent 用户交互
LangChain 可以算是 LLM 时代做 AI 应用开发必备的框架和平台,从模型选择、数据库链接与各种 Agent 搭建等,AI 应用的搭建、运行和管理都可以在 LangChain 上进行。 某种意义上,LangChain 可能是最了解 Agent(智能体)…...

Docker 用例:15 种最常见的 Docker 使用方法
容器化应用程序而不是将它们托管在虚拟机上是过去几年一直流行的概念,使容器管理流行起来。Docker 处于这一转变的核心,帮助组织无缝地采用容器化技术。最近,Docker 用例遍布所有行业,无论规模大小和性质如何。 什么是Docker&…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...

STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...

嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...
虚拟电厂发展三大趋势:市场化、技术主导、车网互联
市场化:从政策驱动到多元盈利 政策全面赋能 2025年4月,国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》,首次明确虚拟电厂为“独立市场主体”,提出硬性目标:2027年全国调节能力≥2000万千瓦࿰…...
多模态图像修复系统:基于深度学习的图片修复实现
多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能
1. 开发环境准备 安装DevEco Studio 3.1: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK 项目配置: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...

STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...