【Linux】进程信号(2)_信号捕捉_中断

hello~ 很高兴见到大家! 这次带来的是Linux系统中关于信号这部分的一些知识点,如果对你有所帮助的话,可否留下你宝贵的三连呢?
个 人 主 页 : 默|笙


文章目录

  • 一、信号捕捉
    • [1.1 信号捕捉的流程](#1.1 信号捕捉的流程)
    • [1.2 硬件中断](#1.2 硬件中断)
    • [1.3 时钟中断](#1.3 时钟中断)
    • [1.4 死循环](#1.4 死循环)
    • [1.5 软中断](#1.5 软中断)
    • [1.6 缺页中断?除零野指针错误?](#1.6 缺页中断?除零野指针错误?)

一、信号捕捉

1.1 信号捕捉的流程

  1. 处理信号不会立即处理,而是会在合适的时候进行处理?那么具体是什么时候呢?是系统从内核态返回用户态的前夕会进行信号的检测和处理
  1. 首先来浅显地讲一下什么是用户态和内核态。之前讲虚拟地址空间的时候,有一块区域没有讲过,那就是内核空间。以 32 位系统为例,0~3GB 是用户空间,进程处于用户态时只能访问这一块空间;3~4GB 是内核空间,只有进程进入内核态后才可以访问这一块空间。内核态的权限要比用户态更高
  1. 首先在执行主控制流程的某条指令时,因为中断、异常或系统调用进入内核(关于中断和异常之后会讲到),这里我们就拿系统调用来说,我们使用系统调用需要访问内核空间,因此当前进程会切换为内核态;内核执行完这个系统调用,准备从内核态回到用户态时,会检查当前进程是否存在待递送的信号。如果信号处理方式为忽略或默认,内核在当前内核态下就可以完成处理;但如果处理方式是自定义的,就必须先返回用户态,执行对应的自定义函数;函数执行完毕后,会再次进入内核态,最终流程结束后返回用户态,继续执行主程序
  2. 按理来说,内核态比用户态权限要高,看似没必要切换为用户态执行自定义函数;这么做的核心原因是禁止在内核态执行用户代码:信号的自定义处理函数是由用户编写的代码,这段代码不受系统信任,如果直接在内核态运行,一旦出现非法操作、恶意代码,就会随意修改内核数据、破坏系统稳定,甚至导致整个操作系统崩溃。因此必须切回用户态执行,用权限隔离来约束用户代码,保证内核安全。
  1. 进程处理自定义信号函数一共会发生四次状态切换,可以用此图辅助记忆(无限符号 + 横线,无限符号交点要在横线下方)。横线上方是用户态,横线下方是内核态,无限符号与横线的四个交点代表4次状态转换:第一次是进程因中断、异常或系统调用,从用户态 → 内核态;第二次是为执行自定义信号函数,从内核态 → 用户态;第三次是函数执行完毕后,再次从用户态 → 内核态做收尾;第四次是全部流程结束,最终从内核态 → 用户态继续运行主程序。而检查未决信号(pending 表),正是发生在内核态切回用户态的前夕,位于无限符号的中心点。

1.2 硬件中断

  1. 我们知道,在遇到 scanf 时,进程会阻塞等待键盘输入,那么操作系统怎么知道输入完成了呢?------> 键盘通过硬件中断把按键信息通知给操作系统,按下回车就代表输入完成;这种硬件主动告知操作系统的机制,就是硬件中断,接下来我们详细讲解它。
  1. 外设(如键盘)就绪后,会向中断控制器发出中断请求信号(一个高频脉冲);中断控制器根据请求来源,确定这个外设对应的唯一中断号,再向 CPU 发送中断通知。CPU 响应中断并获取中断号,即可判断出需要服务的外设。
  2. 此时 CPU 往往正在执行其他进程的任务,为处理该中断请求,它会暂停当前工作,将当前进程的硬件上下文(CPU 寄存器内容)保存到内核栈中,这一过程称为 CPU 保护现场。需要注意的是,这并不是进程切换,后续 CPU 仍然会回到这个进程继续执行。
  3. 针对各类外设的中断服务程序,早已由操作系统预先实现,中断向量表中保存着这些程序的入口地址。CPU 只需依据中断号查表,找到并执行对应的中断服务程序即可。待外设的中断处理任务完成后,CPU 会恢复之前保存的上下文,继续执行原进程
  1. 中断控制器(了解) :所有外设的中断请求先统一接到它这里,它负责给每个外设分配对应的中断号,再按优先级把中断发给 CPU,相当于帮 CPU 统一管理所有外设的 "打断请求"
  2. 中断向量表(现代升级成中断描述符表IDT)本质上就是一个函数指针数组,数组的下标直接对应中断号,数组中存储的元素是对应中断服务程序的入口地址。它是操作系统的一部分,操作系统一启动就加载到内存中了,所以说是内置好的。
  1. 之前讲过的冯・诺依曼体系结构中,数据信号不会由 CPU 直接与外设交互,CPU 只与存储器进行数据往来;但控制信号有所不同:CPU 可以通过控制信号主动管控外设,外设也能借助硬件中断,反向打断并通知 CPU 进行处理。
  2. 中断只会在极短的瞬间执行中断服务程序,CPU 的绝大部分时间都在执行进程任务。也就是串行运行关系,下面的时钟中断也是这样,毕竟时钟中断是硬件中断的一种。

如图所示:

  1. 红色线段:代表 CPU 正常执行进程任务的时间,是系统运行的主体部分。
  2. 黑色竖线:代表响应中断、执行中断服务程序的时间,仅占极小比例,体现了中断处理的 "快速响应、快速归还" 特性。

这是因为中断是异步、抢占式的事件:CPU 仅在收到中断请求时,短暂切换到中断上下文完成外设服务,处理完成后会立即恢复原进程的上下文,继续执行进程任务。因此,从宏观时间尺度看,中断处理几乎是 "一瞬间" 的操作,不会长时间占用 CPU 资源。

  1. 由外部硬件设备主动触发,中断系统运行,就称为硬件中断。借助硬件中断机制,操作系统无需再对硬件状态进行周期性的轮询检测:在没有中断的场景下,由于操作系统无法预知硬件何时需要服务,只能通过 "一遍遍主动询问" 的轮询方式检查硬件状态,这会极大浪费 CPU 资源。
  2. 有没有发现,信号特别像硬件中断?没错,先诞生的是硬件中断,信号是操作系统模仿硬件中断机制创造出来的软件中断;硬件中断也支持屏蔽操作,但这里就不细讲了。

1.3 时钟中断

介绍

  1. 进程可以在操作系统的统一指挥下,被执行、调度。那么操作系统本身又由谁控制?操作系统是运行在硬件之上的系统程序,一个软件,它的 "指挥棒" 正是硬件时钟源。硬件时钟源会以固定频率产生周期性的中断信号,这就是时钟中断。例如,若时钟中断频率为 100kHz(即每 10 微秒触发一次中断),那么累计触发 100 次中断,就恰好对应 1 毫秒的时间流逝。操作系统就能够通过这个中断次数来统计时间,这个时钟源就相当于是操作系统的心脏。

因为有时钟源硬件,计算机才能在没有网络的情况下计时。计算机主板上有一颗纽扣电池,专门为实时时钟硬件供电,所以电脑关机一段时间后,开机显示的时间依然准确;但如果关机时间太长导致纽扣电池电量耗尽,计算机的时间就会出现问题。起始的时间开机获取,当前时间 = 起始时间 + 时间戳。

  1. 时钟源硬件在现代已经集成到 CPU 内部,不再需要经过外部中断控制器作为中介来与 CPU 通信,效率更高。

时间片

  1. 所以时间片本质是什么东西?它就是一个计数器。操作系统依靠时钟源感知时间流逝,进程被调度运行时会获得一个初始计数值;每经过一个时钟周期,计数器就减 1,当计数器减到 0 时,操作系统就会触发进程切换,把 CPU 交给其他进程。它就在task_struct里面:
cpp 复制代码
struct task_struct {
    // 进程时间片(计数器,时钟中断每次 -1)
    long counter;
    // ...其他字段
};
  1. current 指针会指向当前正在运行的进程,操作系统通过它找到要控制的 task_struct 中的 counter;每次时钟中断让 counter 递减,减到 0 就触发调度,这就叫做时间片耗尽。
  1. 我们电脑里的主频,本质是基于时钟源内部晶振的基准频率,经 CPU 倍频后得到的工作频率。时钟源由时钟发生器与时钟计数器构成:时钟发生器依靠晶振产生稳定的振荡信号,时钟计数器则设定每振荡多少次就触发一次时钟中断(分频功能),二者配合,就能精确控制时钟中断的触发频率。CPU 主频越高,运算速度就越快,整体系统效率也就越高。

1.4 死循环

  1. 如果是这样的话,操作系统岂不是不用做任何事情?它需要什么处理方法就预先注册到中断向量表(IDT)里面就好,有设备或程序需要服务时,操作系统会根据中断号在中断向量表中找到对应程序入口,再交由 CPU 去执行。操作系统的本质,就是一个等待中断的死循环

平时躺平死循环等待中断,中断来了就查 IDT 执行对应的处理逻辑,干完活继续躺平。

  1. 它的本体:
cpp 复制代码
for (;;)
{
    // 暂停执行,被动等待中断唤醒
    pause();
}

1.5 软中断

  1. 上面讲到的是由外部硬件触发的中断,那能不能够通过内部软件进行触发呢?当然可以。CPU 内置了专门用于触发中断的特殊汇编指令(比如 int $0x80 / syscall),程序员可以在 C/C++ 代码中嵌入这类汇编指令,主动触发软件中断(C/C++ 支持与汇编混编,它们最终都会被编译器编译成 CPU 可执行的二进制文件)。
  2. 为什么要有软中断呢?是为了提供系统调用接口。操作系统里面有一张系统调用函数指针表,对应硬件中断的中断向量表,它的下标对应每一个系统调用的系统调用号。
  3. 谁来做软中断之前的所有工作?C 语言标准库做的。其实我们日常使用的所有系统调用函数,都被 C 标准库做了封装,库函数把系统调用号和触发软中断的专用汇编指令全部封装在了内部。所以我们执行系统调用函数时,参数少就直接存入 CPU 寄存器,参数多就放到栈空间,系统调用号固定存入指定寄存器;最后执行汇编指令触发软中断,CPU 会根据系统调用号,到内核的系统调用函数指针表中找到对应的内核函数并执行
  4. 操作系统怎么把返回值给用户程序?简单的返回值通过 CPU 寄存器 直接传递,大量的数据则通过 用户程序预先传入的缓冲区地址 写入返回。

1.6 缺页中断?除零野指针错误?

  1. 缺页中断?除零错误?野指针非法访问?这些问题由硬件发现再汇报给OS,之后全部都会被转换成为 CPU 内部的软中断(异常),然后走中断处理例程完成所有处理。缺页中断会申请内存、填充页表、完成虚拟地址到物理地址的映射;除零、野指针这类错误会给目标进程发送信号,最终杀掉进程;内存碎片的处理并非由 CPU 中断触发,而是操作系统内存管理器主动完成的工作
  2. 也就是说,每一个问题的解决方案,都早已被操作系统预先编写并注册好了;当 CPU 硬件检测到问题、触发软中断(异常)时,会自动找到这个提前备好的处理方法并执行,完成对应的修复或善后工作即可。
  3. CPU 内部主动触发的中断:
  1. 像 int $0x80、syscall 这类程序主动发起、非报错的、用来进入内核的,叫做陷阱(Trap),不只系统调用。
  2. 像除零、野指针、缺页这类执行出错、被动触发的,叫做异常(Exception)。
  1. 所以操作系统就是基于中断处理的软件集,它几乎所有的所有工作都依靠中断来驱动完成。

今天的分享就到此结束啦,如果对读者朋友们有所帮助的话,可否留下宝贵的三连呢~~
让我们共同努力, 一起走下去!

相关推荐
东方不败之鸭梨的测试笔记2 小时前
UI自动化执行时,元素不在视野内,需要拖动滑动条才能找到,这种元素怎么处理?
运维·ui·自动化
图灵机z2 小时前
【操作系统】四、进程管理
linux·服务器·网络·windows·macos·centos·risc-v
桌面运维家2 小时前
服务器安全:异常流量监控与DDoS溯源指南
服务器·安全·ddos
新新学长搞科研2 小时前
【高届数人工智能会议】第七届人工智能、网络与信息技术国际学术会议(AINIT 2026)
运维·网络·人工智能·计算机网络·自动化·信号处理·可信计算技术
KKKlucifer2 小时前
特权账号管理与运维安全审计核心技术
运维·安全
haaaaaaarry2 小时前
【操作系统】第三章 内存管理(一)
linux·考研·操作系统
牛奶咖啡132 小时前
DevOps自动化运维实践_基于Cobbler搭建UEFI网络引导的自动安装平台
linux·运维·自动化·uefi·pxe·uefi网络引导自动安装平台·tftp dhcp 环境搭建
云飞云共享云桌面2 小时前
东莞智能装备工厂10个solidworks共享一台服务器做装配体设计
运维·服务器·网络·云计算·电脑
xuxie992 小时前
N12 arm-clk时钟
运维·服务器·网络