【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参数输出

完.

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

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

相关文章

盘点2024年4款高清稳定的Windows10录屏工具。

Windows10电脑录屏在生活当中还是挺重要的&#xff0c;无论是教育领域的制作教程&#xff0c;还是游戏玩家记录精彩瞬间&#xff0c;亦或是商务人士进行演示&#xff0c;录屏都能发挥巨大作用。如果设备自带的一些工具无法完成录屏需求的话&#xff0c;这里帮大家找了几款好用到…

AI大模型应用(3)开源框架Vanna: 利用RAG方法做Text2SQL任务

AI大模型应用(3)开源框架Vanna: 利用RAG方法做Text2SQL任务 RAG&#xff08;Retrieval-Augmented Generation&#xff0c;如下图所示&#xff09;检索增强生成&#xff0c;即大模型LLM在回答问题时&#xff0c;会先从大量的文档中检索出相关信息&#xff0c;然后基于这些检索出…

万家数科:零售业务信息化融合的探索|OceanBase案例

本文作者&#xff1a;马琳&#xff0c;万家数科数据库专家。 万家数科商业数据有限公司&#xff0c;作为华润万家旗下的信息技术企业&#xff0c;专注于零售行业&#xff0c;在为华润万家提供服务的同时&#xff0c;也积极面向市场&#xff0c;为零售商及其生态系统提供全面的核…

挖矿病毒来势汹汹

病毒来了, 我的个人站点使用了 wordpress, 它的不知哪个漏洞让黑客攻入了我的站点 使用 top 命令看到了有不明进程始终占据了 100% 的 CPU snapshot 1 snapshot 2 通过以下 "三板斧"可以查杀这个进程 先用 top (shiftp) 查找占据 CPU 最多的进程根据其进程号 pid 查看…

【数据结构】宜宾大学-计院-实验四

栈和队列之&#xff08;栈的基本操作&#xff09; 实验目的&#xff1a;实验内容&#xff1a;实验结果&#xff1a;实验报告:&#xff08;及时撰写实验报告&#xff09;&#xff1a;实验测试结果&#xff1a;代码实现1.0&#xff1a;&#xff08;C/C&#xff09;【含注释】代码…

QGIS之三十二DEM地形导出三维模型gltf

效果 1、准备数据 (1)dem.tif (2)dom.tif 2、qgis加载dem和dom数据 3、安装插件 插件步骤可以参考这篇文章 QGIS之二十四安装插件 安装了Qgis2threejs插件,结果

无人机之自主降落系统篇

一、定义与功能 无人机自主降落系统是指无人机在无需人工干预的情况下&#xff0c;按照预先设定好的程序或基于实时感知的环境信息&#xff0c;自主完成降落过程的技术系统。该系统能够确保无人机在完成任务后安全、准确地降落到指定位置。 二、系统组成 无人机自主降落系统主…

ELK之路第二步——可视化界面Kibana

Kibana 1.安装2.解压3.修改配置4.启动 这部分内容就比较简单了&#xff0c;水一片文章。 1.安装 需要梯子 官网下载链接&#xff1a;https://www.elastic.co/cn/downloads/past-releases/kibana-7-3-0 如果你去官网下载页面&#xff0c;点击下载是404报错&#xff0c;记得切换…

redis的zset实现下滑滚动分页查询思路

常规zset查询 我们redis的数据为 我们知道 我们常规查询的话 我们假如 zset 表中 有7个元素&#xff0c;然后我们进行分页查询的话&#xff0c;我们一次查3个元素&#xff0c;然后查出来元素 和元素的分数 我们redis的语法应该这样写 zrevrangebyscore wang 1000 0 withsc…

Flutter 12 实现双击屏幕显示点赞爱心多种动画(AnimationIcon)效果

本文主要是使用Flutter封装一个双击屏幕显示点赞爱心UI效果&#xff0c;并实现了爱心Icon 透明度、缩放、旋转、渐变等动画效果。 实现效果&#xff1a; 实现逻辑&#xff1a; 1、封装FavoriteGesture&#xff08;爱心手势&#xff09;实现双击屏幕显示爱心Icon&#xff1b; …

【设计模式系列】抽象工厂模式

一、什么是抽象工厂模式 抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;是一种创建型设计模式&#xff0c;它提供了一个接口&#xff0c;用于创建一系列相关或相互依赖的对象&#xff0c;而无需指定它们具体的类。这种模式允许客户端使用抽象的接口来创建一组…

VoLTE 微案例:VoLTE 注册失败,I-CSCF 返回 403,HSS(UAR) 返回 5001

目录 1. 问题描述 2. 故障注册流程与正常流程对照 3. 结论 博主wx:yuanlai45_csdn 博主qq:2777137742 想要 深入学习 5GC IMS 等通信知识(加入 51学通信),或者想要 cpp 方向修改简历,模拟面试,学习指导都可以添加博主低价指导哈。 1. 问题描述

对比迁移项目的改动

文章目录 对比迁移项目的改动场景背景解决方案 对比迁移项目的改动 场景背景 同源定制化项目&#xff0c;同一套代码扩展出来的项目&#xff08;从领导口中得知&#xff09; A项目的有三维地图展示&#xff0c;项目B跑起来却加载不出来&#xff0c;但是本地运行A项目代码&…

vue-pc 实现内嵌式微信扫码登录(附完整代码)

一、准备工作 1. 注册微信开放平台账号 地址&#xff1a;&#xff08;[https://open.weixin.qq.com/](https://open.weixin.qq.com/)&#xff09; 2. 申请开发者资质认证&#xff1a; 3. 创建网站应用 4. 查看应用详情&#xff0c;拿到 AppID 、 AppSecret 和 redirect_uri (授…

如何在服务器上部署开源大模型 GLM-4-9B-Chat 并应用到RAG应用中

本地服务器部署开源大模型有一个前提&#xff0c;就是得有 GPU 显卡资源&#xff0c;在我下面的例子中我租用了 autodl 中的算力资源&#xff0c;具体是租用了一张消费级别的 RTX 3090 显卡。 环境配置 操作系统及版本&#xff1a;ubuntu 22.04CUDA 版本&#xff1a; 12.1pyto…

【MATLAB源码-第263期】基于matlab的帝企鹅优化算法(EPO)无人机三维路径规划,输出做短路径图和适应度曲线.

操作环境&#xff1a; MATLAB 2022a 1、算法描述 帝企鹅优化算法&#xff08;Emperor Penguin Optimizer&#xff0c;简称EPO&#xff09;是一种基于自然现象的优化算法&#xff0c;灵感来自于帝企鹅在南极极寒环境中的生活习性。帝企鹅是一种群居动物&#xff0c;生活在极端…

再创佳绩 | 竹云荣获“数据要素×”大赛黑龙江分赛一等奖!

近日&#xff0c;由国家数据局、黑龙江省人民政府指导&#xff0c;黑龙江省发改委、黑龙江省数据局主办的2024年“数据要素”大赛黑龙江分赛决赛盛大召开。竹云作为联合单位参与《供热数据资产登记评价中心供热数据要素综合服务平台》项目&#xff0c;荣获绿色低碳赛道一等奖。…

C++ [项目] 愤怒的小鸟

现在才发现C游戏的支持率这么高&#xff0c;那就发几篇吧 零、前情提要 此篇为 制作,由于他没有CSDN,于是由我代发 一、基本介绍 支持Dev-C5.11版本(务必调为英文输入法),基本操作看游戏里的介绍,怎么做的……懒得说,能看懂就看注释,没有的自己猜,如果你很固执……私我吧 …

基于K8S的StatefulSet部署mysql主从

StatefulSet特性 StatefulSet的网络状态 拓扑状态&#xff1a;应用的多个实例必须按照某种顺序启动&#xff0c;并且必须成组存在&#xff0c;例如一个应用中必须存在一个A Pod和两个B Pod&#xff0c;且A Pod必须先于B Pod启动的场景 存储状态&#xff1a;应用存在多个实例&…

分享Vue3中的一个路由加载函数,基于Glob导入模式,根据路径自动生成路由

哈喽&#xff0c;大家好&#xff01;我是「励志前端小黑哥」&#xff0c;我带着最新发布的文章又来了&#xff01; 专注前端领域10年&#xff0c;专门分享那些没用的前端知识&#xff01; 今天要分享的内容&#xff0c;是一段路由加载的函数代码&#xff0c;这段代码能自动读取…