Linux 信号捕捉

我们知道信号的处理不是即时的,进程在合适的时机才会处理信号,而这个时机就比如从内核态返回用户态。

1. 用户态与内核态

在操作系统中,用户态(User Mode)和内核态(Kernel Mode)是两种不同的CPU运行模式,它们决定了进程可以执行的操作范围和对系统资源的访问权限。

用户态是进程执行普通应用程序代码的状态。在用户态下,进程的权限受到限制,不能直接访问硬件设备或执行特权指令。用户态进程通常只能访问分配给它的内存空间,以及通过操作系统提供的系统调用接口进行受控的资源访问。

内核态是操作系统的核心部分运行的状态,也称为特权态或监管态。在内核态下,操作系统拥有对所有硬件资源的完全访问权限,可以执行任何CPU指令。内核态用于执行需要高权限的操作,如内存管理、进程调度、硬件设备驱动等。

进程地址空间被分为两部分:用户空间与内核空间,每个空间都有自己的页表去映射内存,内核空间使用的页表叫做内核级页表,用户空间使用的页表叫做用户级页表。内核级页表在整个内存中指保留一份。

内核空间中的虚拟地址,指向了内存中的操作系统的代码和数据,操作系统本身也是一个软件,也要有自己的代码和数据,任何用户访问操作系统的本质,其实都是去执行操作系统的代码。用户访问操作系统,其实就是通过地址空间的内核空间区域来访问的!当一个进程想要访问操作系统,就可以通过自己的地址空间的内核部分来访问。

每个进程都有自己的独立的地址空间,那么每个进程都有自己独立的内核空间,因此每个进程都可以访问到操作系统!也就是说,内核空间存在的意义在于,不论当前哪一个进程在调度,都可以随时通过该进程的内核空间来找到操作系统!

可以简单理解为:当进程执行用户空间的代码,此时就处于用户态,当进程执行内核空间的代码,此时就处于内核态。当然,其实此处并不是进程自己去执行内核空间的代码,而是唤醒操作系统去执行。最简单的例子就是系统调用,当进程调用系统调用的时候,此时需要更高级别的权限来访问内核的底层数据。毫无疑问普通的进程是没有这个权限的,当进程进行系统调用,此时就会唤醒操作系统去执行内核空间的代码,此时就完成了用户态到内核态的切换。

2. 处理过程

在从内核态返回用户态之前,操作系统就会去处理信号。

当操作系统因为某些原因陷入内核后,会先处理用户的需求,当处理完需求后,就会检测当前是否有需要处理的信号。也就是上图的C部分,该过程就是检测是否有信号要处理。

检测的结果有三种:

  • 没有要处理的信号,直接返回用户态
  • 有要处理的信号,且该信号的处理方式是默认处理函数,那么直接在内核态处理该信号,处理完毕后返回(C->D->A)
  • 有要处理的信号,且该信号的处理方式是用户自定义函数,那么要先切换回用户态执行自定义函数(E),执行完函数后再到内核态(F),最后再切换回用户态(A)

如果信号的处理方式是默认处理方式,此时直接在内核态执行代码,主要有两个原因:

  • 信号的默认处理方式,是操作系统自己提供的,因此不会有安全性问题,可以直接以内核态的高级权限执行
  • 大部分信号的默认处理方式,是直接杀掉当前进程,杀掉进程的行为,需要内核态的权限,因此直接在内核态就可以杀掉这个进程

当信号的处理方式是用户自定义函数,那么要先切换回用户态执行,因为用户自定义的handler函数,其安全性是不确定的,如果贸然给这个函数一个内核态的权限,用户有可能会拿高级权限去做不安全的事情,所以不能给用户自定义的函数内核态权限,而是回到用户态执行这个函数。

那么下一个问题就是,执行完handler函数后,E已经在用户态了,为什么还要回到内核态,在回到用户态?

比如说某个时刻,进行了A -> B的陷入内核过程,那么当B执行完毕后,就要回到A。所以在内核态B中一定会存储一条信息(准确来说叫做上下文),指明之前A执行到那一行代码,从而在B -> A的时候,可以知道跳转回哪里。

当用户态进程陷入内核态时,内核会保存用户态进程的上下文信息,包括:

  • 寄存器值:例如程序计数器(PC)、堆栈指针(SP)、通用寄存器等。
  • 内存状态:例如内存页表、虚拟地址空间等。
  • 其他状态:例如进程状态、信号掩码等。

内核通过保存这些上下文信息,可以记录用户态进程执行到哪个位置,以及该进程的运行状态。当内核处理完用户态进程的请求后,会恢复用户态进程的上下文信息,并将控制权返回给用户态进程。

用户态进程恢复执行后,会从之前中断的位置继续执行,而不会意识到自己曾经陷入内核态。也就是说,陷入内核态之后,只有内核态知道之前的用户态执行到哪里,所以E状态下不能直接跳转回原来执行的地方,必须先回到内核态,去找到原先执行的位置,在返回用户态。

而每一次在从内核态返回用户态之前,操作系统都会处理信号。

如上图中一共发生了两次信号处理,即 C->EF->A,这两个时候都会检测并处理信号。

相关推荐
运维&陈同学1 小时前
【Elasticsearch05】企业级日志分析系统ELK之集群工作原理
运维·开发语言·后端·python·elasticsearch·自动化·jenkins·哈希算法
シ風箏2 小时前
Neo4j【环境部署 02】图形数据库Neo4j在Linux系统ARM架构下的安装使用
linux·数据库·arm·neo4j
ZVAyIVqt0UFji4 小时前
go-zero负载均衡实现原理
运维·开发语言·后端·golang·负载均衡
Cachel wood4 小时前
Vue.js前端框架教程8:Vue消息提示ElMessage和ElMessageBox
linux·前端·javascript·vue.js·前端框架·ecmascript
小屁不止是运维7 小时前
麒麟操作系统服务架构保姆级教程(二)ssh远程连接
linux·运维·服务器·学习·架构·ssh
gavin_gxh8 小时前
SAP PP ECN CSAP_MAT_BOM_MAINTAIN
运维·经验分享·其他
黑客K-ing8 小时前
网络安全防范
linux·服务器·web安全
这题怎么做?!?9 小时前
ARP协议及其具体过程
运维·服务器·网络
Lay_鑫辰9 小时前
禾川HCQ1系列PAC脉冲控制步进驱动器
运维·人工智能·单片机·嵌入式硬件·自动化
卡卡大怪兽9 小时前
fastAPI接口的请求与响应——基础
服务器·网络·fastapi