news 2026/7/18 13:27:06

Re:Linux系统篇(四十四)信号篇·二:Linux 信号的产生原理:硬件异常、软件条件与内核处理机制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Re:Linux系统篇(四十四)信号篇·二:Linux 信号的产生原理:硬件异常、软件条件与内核处理机制

◆ 博主名称: 小此方-CSDN博客
大家好,欢迎来到小此方的博客。
⭐️Linux系列个人专栏: 【主题曲】Linux
⭐️此方的GitHub: github_此方
⭐️Re系列专栏:我们思考 (Rethink) · 我们重建 (Rebuild) · 我们记录 (Record)

文章目录

  • 概要&序論
  • 一、 硬件异常产生信号
    • 1.1什么是硬件异常
    • 1.2硬件异常举例
      • 1.2.1浮点数错误
      • 1.2.2段错误
    • 1.3 CPU是怎么知道硬件错误
      • 1.3.1算术溢出与除0错误
      • 1.3.2 内存访问段错误
    • 1.4 MMU 查表时的权限与状态判定
      • 1.4.1 根本没有映射(空指针引发的未分配错误)
      • 1.4.2 有映射但越界或越权(非法访问限制区域)
    • 1.5 操作系统的接管与进程上下文读取
    • 1.6 合法与非法的甄别,产生信号
  • 二、软件条件产生信号
    • 2.1管道产生信号
    • 2.2闹钟产生信号
      • 2.2.1先见一见闹钟怎么用
      • 2.2.2闹钟的用法详细介绍
      • 2.2.3递归闹钟与操作系统的基本运行原理
      • 2.2.4时间戳与时间片
      • 2.2.5操作系统对闹钟的先描述再组织

概要&序論

Hello,大家好我是此方,今天继续介绍信号是如何产生的问题。上一篇我们介绍了信号的两种产生机制:键盘产生和系统调用/命令产生。本文将讲解剩下两类信号产生机制——“硬件异常”与“软件条件”。同时带大家初步接触“时钟中断”等内容。

一、 硬件异常产生信号

1.1什么是硬件异常

在 Linux 操作系统中,程序的崩溃或异常终止(如除0错误、野指针访问)会触发硬件异常,CPU 首先捕捉到硬件异常,并立刻通过中断机制将其移交给操作系统内核。

信号由操作系统发送。

1.2硬件异常举例

1.2.1浮点数错误

8号信号:SIGFPE (Floating-point exception)浮点数异常。

1.2.2段错误

11号信号:SIGSEGV (Segmentation violation / Segmentation fault)段错误。

1.3 CPU是怎么知道硬件错误

操作系统作为软硬件资源的管理者,之所以能知道进程的犯错,得益于 CPU 内部各种寄存器和硬件单元的实时监控

1.3.1算术溢出与除0错误

当程序执行a /= 0时,CPU 的算术逻辑单元(ALU)在计算时会触发异常,并自动将状态寄存器(如 x86 的EFLAGS寄存器)中的特定标志位(例如溢出或错误标志)置位。

1.3.2 内存访问段错误

当程序尝试访问int *p = nullptr; *p = 100;,负责地址翻译的集成硬件MMU(内存管理单元)会拿着该虚拟地址和CR3 寄存器中保存的当前进程页表物理地址去进行转换。MMU 在将虚拟地址转换为真正的物理地址时,主要根据页表项中的“权限和状态”来判断访问是否合法。

1.4 MMU 查表时的权限与状态判定

1.4.1 根本没有映射(空指针引发的未分配错误)

页表项中有一个最关键的位叫Present(存在位,通常是 Bit 0)

  • Present = 1:代表该虚拟地址已经映射了真实的物理内存。
  • Present = 0:代表这块虚拟地址在物理内存中根本不存在。

当 MMU 发现对应行的Present位为 0 时,硬件逻辑电路就会自动触发中断信号(硬件报错)。

1.4.2 有映射但越界或越权(非法访问限制区域)

即使地址存在映射,页表项中还包含R/W(读写权限位)U/S(用户/内核权限位)

  • 如果代码企图去修改(写)一个只读的常量字符串(R/W位为 0),MMU 就会报错。
  • 如果普通应用程序(用户态)企图去访问内核才能看的地址(U/S位为 0),MMU 同样会报错。

1.5 操作系统的接管与进程上下文读取

当 MMU 或 CPU 触发硬件报错后,CPU 会瞬间暂停当前正在执行的应用程序,强行切换到操作系统的缺页异常处理程序。此时,操作系统必须立刻、无条件地读取当前进程的上下文(即内核中的current->task_struct以及 CPU 寄存器),以此弄清楚两件事:是谁在哪个地方犯了错?

在 Linux 内核源码中,current 是一个极其常用的宏或者指针。它永远指向当前正在 CPU 上执行的那个进程的 task_struct。

操作系统会读取以下一组特殊的寄存器:

  • CR2 寄存器:这个寄存器极其重要!MMU 报错时,会自动把导致报错的那个虚拟地址塞进 CR2 里。操作系统读取它,才能知道程序刚才是在访问哪块内存时翻车的。
  • 程序计数器(CS:RIP / EIP):操作系统要看看底是哪一行代码(哪条指令)触发了这次内存访问。
  • 通用寄存器和错误码(Error Code):搞清楚程序当时是在执行“读”还是“写”,以及当时是在用户态还是内核态。

1.6 合法与非法的甄别,产生信号

拿到上下文并读取 CR2 里的虚拟地址后,操作系统会去对比当前进程的VMA(虚拟内存区域,即 vm_area_struct 链表)来甄别这次报错是否合法:

  • 合法错误(真正的缺页):比如使用mmap分配了虚拟内存,但在第一次读写时物理内存还没落实(Present = 0)。操作系统会执行补齐页表的操作,分配物理内存并建立映射,随后恢复上下文,重新执行那条代码,程序得以“复活”。
  • 非法错误(真正的越界/野指针):如果操作系统发现该虚拟地址根本不在进程合法的虚拟内存区域内,或者违反了读写权限。操作系统就会将其判定为非法,并向目标进程发送SIGSEGV(段错误)信号;若是除0错误,则发送SIGFPE(浮点异常)信号。最终强行杀死进程,造成程序崩溃。

二、软件条件产生信号

2.1管道产生信号

软件条件产生信号的方式有很多种,我们曾经学过管道产生信号的方式。Re:Linux系统篇(三十七)通信篇·二:深入理解匿名管道:父子进程是如何“看到同一个文件”


操作系统不会做任何浪费资源的事情。当读端关闭的时候,写段任在写入就毫无意义。此时操作系统必须发送信号SIGPIPE终止进程。

2.2闹钟产生信号

2.2.1先见一见闹钟怎么用

这一块本来应该放在时钟中断讲,但是放在这里也可以。


补充一下pause系统调用,让这个进程处于暂停状态。

#include<iostream>#include<unistd.h>#include<signal.h>voidHander(inti){std::cout<<"收到一个信号"<<i<<std::endl;}intmain(){for(inti=0;i<32;i++)signal(i,Hander);alarm(10);std::cout<<"定一个闹钟"<<std::endl;std::cout<<"开始睡觉"<<std::endl;pause();std::cout<<"睡醒"<<std::endl;return0;}

如上。我们让进程设置一个闹钟。时间到了就会发送14号信号。终止进程。

2.2.2闹钟的用法详细介绍

调用alarm(seconds)后,系统开始倒计时。

  • 时间到了会怎样?系统会向你的程序发送一个SIGALRM 信号
  • 默认后果:如果你的程序没有提前做好准备,收到这个信号后会直接退出(终止运行)
  • 一次只能设一个闹钟:如果先调用了alarm(10),紧接着又调用了alarm(3),前一个闹钟会被直接覆盖。程序只会在 3 秒后收到闹钟信号。
  • 如何取消闹钟:调用alarm(0)即可直接关闭倒计时,不会再触发信号。

2.2.3递归闹钟与操作系统的基本运行原理

我们让闹钟执行的自定义捕捉函数调用新的闹钟。让每一个闹钟响起后执行随机的工作

#include<iostream>#include<unistd.h>#include<signal.h>#include<time.h>#include<vector>#include<functional>std::vector<std::function<void(int)>>work;voidfunc_1(intx){std::cout<<"我是一个网络任务,参数为: "<<x<<std::endl;}voidfunc_2(intx){std::cout<<"我是一个磁盘任务,参数为: "<<x<<std::endl;}voidfunc_3(intx){std::cout<<"我是一个日志任务,参数为: "<<x<<std::endl;}voidfunc_4(intx){std::cout<<"我是一个计算任务,参数为: "<<x<<std::endl;}voidfunc_5(intx){std::cout<<"我是一个数据库任务,参数为: "<<x<<std::endl;}voidHander(inti){std::cout<<"###########################"<<std::endl;work[rand()%5](rand()%100);alarm(1);std::cout<<"###########################"<<std::endl;}intmain(){work.push_back(func_1);work.push_back(func_2);work.push_back(func_3);work.push_back(func_4);work.push_back(func_5);srand((unsignedint)time(NULL));for(inti=0;i<32;i++)signal(i,Hander);alarm(1);//std::cout<<"定一个闹钟"<<std::endl;//std::cout<<"开始睡觉"<<std::endl;while(true)pause();//std::cout<<"睡醒"<<std::endl;return0;}

进程可以处于暂停状态,通过外部信号传达实现不同功能的调用。——这就是操作系统,以及操作系统去做进程管理和内存管理等工作的基本原理

比如拿进程管理为例子,进程被创建好之后PCB就被push_back到一个管理结构中,然后操作系统在运行的时候会去遍历这个管理结构,找到时间片的下一个进程,然后执行他。

操作系统的本质就像是我们这个进程,登录以后就处于一种死循环。操作系统不会去主动执行这些任务。alarm () 间隔时间为0…0几秒,这样的时间间隔,我们的操作系统就会以非常高的频率来执行我们的任务!
操作系统居然不是主动干活的

后面会讲这就是时钟中断

2.2.4时间戳与时间片

时间戳的本质就是一个计数器,外部每一秒刺激操作系统一次,操作系统调用一次就++,实时记录操作系统一共开机了多少时间(操作系统自启动后的时间间隔)

2.2.5操作系统对闹钟的先描述再组织

OS内会不会同时存在很多的闹钟!OS要不要对闹钟进行管理?先描述,在组织!
闹钟结构体的伪代码:

structtimer_list{structlist_headentry;unsignedlongexpires;void(*function)(unsignedlong);unsignedlongdata;structtvec_t_base_s*base;};

我们的很多闹钟会被按照这个时间间隔值(unsigned long expires;(操作系统自启动后的时间间隔))进行建小堆,操作系统对比自己的时间间隔值和闹钟的时间间隔值:1000<1005 不响。1006>1005响。然后调用闹钟的函数,传递信号SIGALRM

新建一个闹钟就是在堆里面插入一个闹钟结构体。但是实际上在内核中,并没有使用这个堆。

闹钟产生信号的这个过程没有硬件参与,这就是软件条件触发信号的另一种方式。扯的有点远了

信号的产生。我们就讲完了。


好的本期内容就到这里,如果对你有帮助,还不要忘记点赞三联支持。我是此方,我们下期再见。bye! Linux、C++、算法持续连载中,欢迎关注WeChat Official Account 【此方的技术栈】。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/18 13:25:32

ChatGPT健身计划生成器上线倒计时:2024年最后一批可免费调用GPT-4o深度优化模板(仅限前500名订阅者)

更多请点击&#xff1a; https://codechina.net 第一章&#xff1a;ChatGPT健身计划生成器的技术定位与演进逻辑 ChatGPT健身计划生成器并非通用大模型的简单应用延伸&#xff0c;而是垂直领域知识工程与对话式AI深度融合的典型范式。其技术定位根植于三个核心支点&#xff1a…

作者头像 李华
网站建设 2026/7/18 13:25:22

Python办公自动化实战:Excel/Word/PDF高效处理

1. 为什么Python是办公自动化的首选&#xff1f; 作为一个每天要处理上百份Excel报表的财务分析师&#xff0c;三年前我第一次接触Python时&#xff0c;完全没想到这个工具会彻底改变我的工作方式。当时我正被月末结账折磨得焦头烂额——手动核对20多个部门的费用明细&#xff…

作者头像 李华
网站建设 2026/7/18 13:25:06

Legacy iOS Kit:终极iOS设备降级、恢复和越狱工具链

Legacy iOS Kit&#xff1a;终极iOS设备降级、恢复和越狱工具链 【免费下载链接】Legacy-iOS-Kit An all-in-one tool to restore/downgrade, save SHSH blobs, jailbreak legacy iOS devices, and more 项目地址: https://gitcode.com/gh_mirrors/le/Legacy-iOS-Kit Le…

作者头像 李华
网站建设 2026/7/18 13:23:09

FastAPI与PostgreSQL高性能API开发实战指南

1. 为什么选择FastAPIPostgreSQL组合 在Python生态中构建API服务时&#xff0c;FastAPI和PostgreSQL的组合正成为越来越多开发者的首选方案。这套技术栈在我过去参与的电商后台系统和物联网数据平台项目中表现尤为出色&#xff0c;其优势主要体现在三个维度&#xff1a; 首先是…

作者头像 李华
网站建设 2026/7/18 13:21:13

如何快速掌握vJoy虚拟摇杆:5个高效秘诀与实战指南

如何快速掌握vJoy虚拟摇杆&#xff1a;5个高效秘诀与实战指南 【免费下载链接】vJoy Virtual Joystick 项目地址: https://gitcode.com/gh_mirrors/vj/vJoy vJoy是一款功能强大的虚拟摇杆软件&#xff0c;能够在Windows系统上模拟真实的游戏手柄或摇杆设备。无论你是游戏…

作者头像 李华
网站建设 2026/7/18 13:20:45

3个核心技术突破让PCL2成为Minecraft启动器的智能管家

3个核心技术突破让PCL2成为Minecraft启动器的智能管家 【免费下载链接】PCL Minecraft 启动器 Plain Craft Launcher&#xff08;PCL&#xff09;。 项目地址: https://gitcode.com/gh_mirrors/pc/PCL 在Minecraft玩家的游戏体验中&#xff0c;启动器扮演着至关重要的角…

作者头像 李华