【Linux】进程信号之信号的处理

news2024/11/27 3:59:52

进程信号 三

  • 一、信号的处理时机
  • 二、内核态与用户态
    • 1、内核态与用户态的转化
    • 2、重谈进程地址空间
  • 三、信号的处理
    • 1、一般信号的处理流程
    • 2、捕捉信号的处理流程
    • 3、信号捕捉函数sigaction

一、信号的处理时机

在前面我们讲过信号产生和保存以后,我们知道进程对于产生的信号不是立即去处理的,而是在"合适"的时候去处理信号,这是因为信号的产生的异步的,当前进程可能正在做更重要的事情!。

那么信号可以被立即处理吗?答案的可以的,但是要满足这个条件:

Linux中如果一个信号之前被阻塞过,当他解除阻塞时,对应的信号会被立即递达!

那么对于进程来说什么是"合适"的时候呢?
答案是:当进程从内核态切换回用户态的时候,进程会在操作系统的指导下,进行信号的检测与处理!

二、内核态与用户态

简单来说内核态与用户态的区别就是:
用户态:进程只能执行用户所写的代码。
内核态:进程只能执行操作系统的代码。

我们知道操作系统也是一款软件,而且是一款专注于搞管理的软件,在对进程进行调度、执行系统调用、异常、中断、陷阱等,都需要借助操作系统,执行操作系统的代码,此时进程便处于内核态。

进程又是如何被调度的呢?

  1. 操作系统的本质:
    • 操作系统也是软件,并且是一个死循环式等待指令的软件。
    • 计算机内部存在一个硬件:时钟模块,每隔一段时间向操作系统发送时钟中断
  2. 进程被调度,就意味着它的时间片到了,操作系统会通过时钟中断,检测到是哪一个进程的时间片到了,然后通过系统调用函数 schedule()保存进程的上下文数据,然后选择合适的进程去运行,这就完成了一次进程调度。

1、内核态与用户态的转化

  • 用户态向内核态的转化的时机:
  1. 进程时间片到了之后,需要进行进程调度时。
  2. 调用系统调用接口,比如 openread
  3. 产生异常、中断、陷阱
  • 内核态向用户态的转化的时机:
  1. 进程调度完成以后。
  2. 系统调用调用完毕时。
  3. 异常、中断、陷阱处理完毕时。

2、重谈进程地址空间

关于进程地址空间的初级知识可以看这里《进程地址空间》
在以前我们只讨论了[0, 3]G的用户空间,并没有对[3, 4]G的内核空间进行讨论,现在我们对[3, 4]G的内核空间进行讨论。

我们在谈论用户空间时提到,用户空间的地址要经过页表映射到物理地址,这个用户空间的页表其实其真实名称是用户级页表,对于内核空间来说也有一张页表,也负责将内核空间的地址映射到物理地址中,这个页表的名称是内核级页表。这两张页表是相互独立的!

内核空间里面存放的是操作系统代码和数据, 所以执行操作系统的代码及系统调用,其实就是在使用这 1 GB 的内核空间

在这里插入图片描述

  1. 对于所有的进程[0, 3]GB是不同的,每一个进程都要有自己的用户级页表用来映射自己的代码和数据。
  2. 所有的进程[3,4]GB是一样的,每一个进程都可以看到同一张内核级页表,所有进程都可以通过统一的窗口,看到同一个操作系统!
  3. 无论进程如何切换,[3,4]GB不变,看到的都是OS的内容,与进程切换无关,也就是说进程切换其实切换的是[0, 3]G的用户空间里面的内容和用户级页表!
  4. 操作系统运行的本质: 其实是在进程的地址空间内运行的!
  5. 由于内核空间中存放的是操作系统的代码和数据,所以调用系统调用的本质: 其实就如同调用动态库中的函数,在自己的地址空间中进行函数跳转并返回即可!

由于操作系统的代码和数据是不能够被轻易访问的,所以在正文代码中如果要执行操作系统的代码和数据,需要先进行状态转化,由用户态转化为内核态,才能成功执行,那么这个状态转换是怎么实现的呢?

对于状态转化,操作系统采用的是软硬件结合的方式。

  • 硬件方面
    CPU中,存在一个 CR3 寄存器,这个寄存器的作用就是用来表是当前处于进程所处的状态。

    CR3寄存器中的值为 3 时:表示处于用户态,可以执行用户的代码。
    CR3寄存器中的值为 0 时:表示处于内核态,可以执行操作系统的代码。

在这里插入图片描述

  • 软件方面
    Linux并没有给我们提供相应的接口让我们可以更改CR3寄存器里面的值,因为操作系统没有办法保证每一个用户使用OS的代码和数据时都要先更改CR3寄存器的值,所以OS提供的所有的系统调用,内部在正执行调用逻辑的时候,会去修改执行级别! 这样就保证了用户使用系统调用的时候用户所处的状态是内核态

三、信号的处理

1、一般信号的处理流程

当CPU正在执行某条代码时,可能因为中断、异常或系统调用进入内核态,然后在内核态完成相应的任务,任务完成以后并不是直接返回用户态,而是调用系统调用do_signal()去处理可以递达信号。

处理信号时会从1号到31号逐个检查block表和pending表,当blockpending表符合处理条件时才进行信号递达

block表pending表是否处理解释
00pending表为0代表该信号没有产生过,无需处理
10block表为0,信号被阻塞,无需处理
11block表为0,信号被阻塞,无需处理
01信号没有被阻塞且pending表为1,代表该信号需要递达

当信号递达时就需要调用handler表里面对应位置的的函数进行执行:

handler表执行动作
SIG_IGN忽略该信号,将该信号的pending表里面的1改为0,然后调用sys _sigreturn()系统调用
进行返回原先中断的位置并恢复为用户态
SIG_DFL执行默认动作:
1. 如果是暂停,就将该进程从运行队列里面取出放到等待队列里面,操作系统开始调度下一个进程。
2. 如果是终止进程,就直接结束该进程,操作系统开始调度下一个进程。

在这里插入图片描述

2、捕捉信号的处理流程

对于被捕捉的信号,与普通信号有所不同,在调用自定义处理方法时,由handler表里面的方法是用户的代码,所以还要进行一次状态转换,转换为用户态,然后执行自定义动作,当自定义动作执行完毕时OS会自动调用一次系统调用sigreturn()使用户态重新陷入内核变成内核态,然后在内核态再调用sys _sigreturn()进行返回并恢复为用户态。

在这里插入图片描述

下面我们通过一张图快速记忆捕捉信号的处理过程:

在这里插入图片描述

ps: 在执行hadler表中的方法之前,操作系统会先将pengding表对应位置的1给清零。

3、信号捕捉函数sigaction

该函数是一个系统调用,功能与signal()函数类似但是功能会更加强大,sigaction函数可以读取修改指定信号相关联的处理动作。

在这里插入图片描述

  • 参数

    1. 第一个参数是要捕捉的信号,第二个与第三个都是一个结构体参数,但是第二个参数是输入型参数,第三个是输出形参数。
    2. act指针非空,则根据act修改该信号的处理动作。若oact指针非空,则通过oact传出该信号原来的处理动作。actoact指向sigaction结构体。
  • 返回值
     调用成功则返回0,出错则返回-1

结构体的定义如下:

在这里插入图片描述

  1. 第一个字段是函数指针,这个函数就是我们捕捉完信号以后要执行的处理动作。
  2. 第二个与第五个字段是实时信号的处理函数,这里我们不做详细解释,可以直接设置为0。
  3. 第三个字段是一个信号屏蔽集,这个字段设置完毕以后我们可以在处理捕捉信号时对信号屏蔽集里面的信号进行屏蔽。
  4. 第四个字段包含了一些选项,一般默认设置为0

关于信号处理时的一些机制:

当某个信号的处理函数被调用时,内核会自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么它会被阻塞到当前处理结束为止。

下面我们来使用该函数验证一下信号处理时:内核会自动将当前信号加入进程的信号屏蔽字。

#include <iostream>
#include <cstring>
#include <unistd.h>
#include <signal.h>

// 打印pending表
void PrintPending(sigset_t set)
{
    std::cout << "当前的pending表:";
    for (int i = 1; i <= 31; i++)
    {
        if (sigismember(&set, i))
        {
            std::cout << '1';
        }
        else
        {
            std::cout << '0';
        }
    }
    std::cout << std::endl;
}

// 自定义处理动作
void handler(int signum)
{
    std::cout << "捕捉到了" << signum << "信号,执行了自定义动作" << std::endl;
    int cnt = 0;
    sigset_t set;
    sigemptyset(&set);
    while (cnt < 5)
    {
        cnt++;
        sigpending(&set);
        PrintPending(set);
        sleep(1);
    }
}

int main()
{
    struct sigaction act, oact;
    memset(&act, 0, sizeof(act));
    memset(&oact, 0, sizeof(act));
    act.sa_handler = handler;
    sigaction(2, &act, &oact);
    while (true)
    {
        sleep(1);
    }
}

这段代码中我们对2号信号进行了捕捉,自定义处理动作就是在自定义函数中停留5秒,每秒都打印一下当前状态的pending表。

我们可以运行程序,然后给该进程发送2号信号触发自定义处理动作,然后再在5秒之内再次发送2号信号观察pending表是否为1,如果为1就代表当前信号收到了阻塞,如果没有变成1代表没有受到阻塞。

在这里插入图片描述

可以看到结果符合我们的理论。

接下来我们尝试利用sigaction3, 4号信号也加入信号屏蔽集中。

#include <iostream>
#include <cstring>
#include <unistd.h>
#include <signal.h>


// 打印pending表
void PrintPending(sigset_t set)
{
    std::cout << "当前的pending表:";
    for (int i = 1; i <= 31; i++)
    {
        if (sigismember(&set, i))
        {
            std::cout << '1';
        }
        else
        {
            std::cout << '0';
        }
    }
    std::cout << std::endl;
}


// 自定义处理动作
void handler(int signum)
{
    std::cout << "捕捉到了" << signum << "信号,执行了自定义动作" << std::endl;
    int cnt = 0;
    sigset_t set;
    sigemptyset(&set);
    while (cnt < 15)
    {
        cnt++;
        sigpending(&set);
        PrintPending(set);
        sleep(1);
    }
}

int main()
{
    struct sigaction act, oact;
    sigset_t set, oset;
    // 进行初始化
    memset(&act, 0, sizeof(act));
    memset(&oact, 0, sizeof(act));
    sigemptyset(&set);
    sigemptyset(&oset);
    // 将3, 4也加入信号屏蔽集中
    sigaddset(&set, 3);
    sigaddset(&set, 4);
    act.sa_handler = handler;
    // 设置信号屏蔽字
    act.sa_mask = set;
    sigaction(2, &act, &oact);

    std::cout << "进程的pid是:" << getpid() << std::endl;
    while (true)
    {
        sleep(1);
    }
}

在这里插入图片描述
如果我们还想将其他信号进行屏蔽,我们可以继续修改sigaction结构体里面sa_mask字段。

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

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

相关文章

YOLOv5、YOLOv8改进: GSConv+Slim Neck

论文题目&#xff1a;Slim-neck by GSConv: A better design paradigm of detector architectures for autonomous vehicles 论文&#xff1a;https://arxiv.org/abs/2206.02424 代码&#xff1a;https://github.com/AlanLi1997/Slim-neck-by-GSConv 在计算机视觉领域&#x…

YOLOv8“炼丹“之扑克牌识别

最近沉迷炼丹, 效果图: 框架Ultralytics YOLOv8 来自GitHub的介绍: Ultralytics YOLOv8 is a cutting-edge, state-of-the-art (SOTA) model that builds upon the success of previous YOLO versions and introduces new features and improvements to further boost pe…

Centos7源码安装redis

1、下载redis Index of /releases/ 2、解压redis tar -xvf redis-6.2.9.tar.gz 3、进入解压后的目录 cd redis-6.2.9/4、指定内存分配器为 libc make MALLOClibc 5、进入src目录&#xff0c;安装 cd src && make install6、运行 ./redis-server 7、添加开机…

IIC延时函数

别人家的程序 void i2c_Start(void) {OLED_I2C_SDA_1(); //SDA 总线置1OLED_I2C_SCL_1(); //SCL 总线置1i2c_Delay(); //延时信号OLED_I2C_SDA_0(); //置 0 i2c_Delay();OLED_I2C_SCL_0(); //SCL 置0i2c_Delay(); }延时函数 static void i2c_Delay(void) {uint8_t…

企业时代下的汽车4S店形势分析

据网上数据显示&#xff0c;2022年约有2000家汽车4S店闭店退网&#xff0c;这一数据不由令人惊叹&#xff01; 疫情放开后&#xff0c;原以为汽车经销商的春天也即将来临&#xff0c;可它们有些已经死在了半路上。 2023年伊始&#xff0c;经销商大戏以一则破产消息开幕——浙…

NR700 —基础知识

01 中国5G频段分布及700M频谱 中国运营商频段分布&#xff1a; 不同频段的无线电波的特征&#xff1a; 700M网络因其低频特性&#xff0c;有着极佳的覆盖能力和穿透能力&#xff0c;但同时相对运营商已有的高频网络有着明显的性能差距。因此700M网络更适合用于底层网络深度覆盖…

mac harbor的安装

harbor的安装 为什么要整这个呢&#xff0c;因为我在学习k8s&#xff0c;但是需要一个自己的镜像仓库。于是&#xff0c;最开始想到的就是在本地直接部署一个&#xff0c;还比较安全、快速。 直接下载了官方的项目&#xff0c;运行脚本发现出了异常&#xff0c;这种异常我已经…

帮群里一位留学生订机票,省了2.2万元

注&#xff1a;此篇文章为峰哥环游世界番外篇&#xff0c;我会记录很多我认为值得分享的图文。在环游世界交流群里的同学记得扫描下方二维码直接观看&#xff0c;不要付费&#xff0c;具体事宜可以看贴&#xff1a;付费文章说明&#xff01; 想进一步了解我环游世界的故事&…

React 之 Suspense和lazy

一. Suspense 参考链接&#xff1a;https://react.docschina.org/reference/react/Suspense suspense&#xff1a;n. 焦虑、悬念 <Suspense> 允许你显示一个退路方案&#xff08;fallback&#xff09;直到它的所有子组件完成加载。 <Suspense fallback{<Loadin…

history记录日期时间和日志记录操作

history命令能查看到操作日期和时间的配置方法&#xff1a; 1&#xff09;在/etc/profile文件中添加一行&#xff1a; export HISTTIMEFORMAT"%F %T whoami " 2&#xff09;保存后&#xff0c;执行加载命令&#xff1a; source /etc/profile 3&#xff09;然后检…

“编写一次,无限应用:深入理解C++模板“

&#x1f680;write in front&#x1f680; &#x1f4dc;所属专栏&#xff1a; C学习 &#x1f6f0;️博客主页&#xff1a;睿睿的博客主页 &#x1f6f0;️代码仓库&#xff1a;&#x1f389;VS2022_C语言仓库 &#x1f3a1;您的点赞、关注、收藏、评论&#xff0c;是对我最大…

E8—Aurora 64/66B ip实现GTX与GTY的40G通信2023-08-12

1. 场景 要在贴有K7系列FPGA芯片的板子和贴有KU系列FPGA芯片的板子之间通过光模块光纤QSFP实现40G的高速通信。可以选择的方式有多种&#xff0c;但本质的方案就一种&#xff0c;即实现4路GTX与GTY之间的通信。可以选择8B/10B编码通过GT IP核实现&#xff0c;而不能通过Aurora…

深度学习(36)—— 图神经网络GNN(1)

深度学习&#xff08;36&#xff09;—— 图神经网络GNN&#xff08;1&#xff09; 这个系列的所有代码我都会放在git上&#xff0c;欢迎造访 文章目录 深度学习&#xff08;36&#xff09;—— 图神经网络GNN&#xff08;1&#xff09;1. 基础知识2.使用场景3. 图卷积神经网…

基于kubeasz部署高可用k8s集群

在部署高可用k8s之前&#xff0c;我们先来说一说单master架构和多master架构&#xff0c;以及多master架构中各组件工作逻辑 k8s单master架构 提示&#xff1a;这种单master节点的架构&#xff0c;通常只用于测试环境&#xff0c;生产环境绝对不允许&#xff1b;这是因为k8s集群…

C++学习笔记——从面试题出发学习C++

C学习笔记——从面试题出发学习C C学习笔记——从面试题出发学习C1. 成员函数的重写、重载和隐藏的区别&#xff1f;2. 构造函数可以是虚函数吗&#xff1f;内联函数可以是虚函数吗&#xff1f;析构函数为什么一定要是虚函数&#xff1f;3. 解释左值/右值、左值/右值引用、std:…

探索FSM (有限状态机)应用

有限状态机&#xff08;FSM&#xff09; 是计算机科学中的一种数学模型&#xff0c;可用于表示和控制系统的行为。它由一组状态以及定义在这些状态上的转换函数组成。FSM 被广泛用于计算机程序中的状态机制。 有限状态机&#xff08;FSM&#xff09;应用场景 在各种自动化系统…

LVGL学习笔记 29 - LED

目录 1. 设置颜色 2. 设置OFF颜色 3. 设置对比度 4. 改变状态 功能类似CheckBox&#xff0c;用一个方形或则圆形的控件显示开关状态。 lv_obj_t* led1 lv_led_create(lv_scr_act());lv_obj_t* led2 lv_led_create(lv_scr_act());lv_obj_align(led1, LV_ALIGN_CENTER, -80…

生产执行MES系统:提升企业灵活性和响应速度的关键利器

在竞争激烈的市场环境下&#xff0c;企业需要不断提高其灵活性和响应速度&#xff0c;以适应快速变化的需求和市场动态。生产执行MES&#xff08;Manufacturing Execution System&#xff09;系统作为信息技术的重要应用&#xff0c;为企业提供了强大的工具和平台&#xff0c;能…

redis 数据结构(一)

Redis 为什么那么快 redis是一种内存数据库&#xff0c;所有的操作都是在内存中进行的&#xff0c;还有一种重要原因是&#xff1a;它的数据结构的设计对数据进行增删查改操作很高效。 redis的数据结构是什么 redis数据结构是对redis键值对值的数据类型的底层的实现&#xff0c…

金蝶云星空与金蝶云星空对接集成采购入库查询连通采购入库新增(MW_写入测试)

金蝶云星空与金蝶云星空对接集成采购入库查询连通采购入库新增(MW_写入测试) 对接源平台:金蝶云星空 金蝶K/3Cloud在总结百万家客户管理最佳实践的基础上&#xff0c;提供了标准的管理模式&#xff1b;通过标准的业务架构&#xff1a;多会计准则、多币别、多地点、多组织、多税…