【Linux】从内存布局到信号屏蔽:Linux 内核态与用户态交互核心知识点汇总
前言:欢迎各位光临本博客,这里小编带你直接手撕,文章并不复杂,愿诸君耐其心性,忘却杂尘,道有所长!!!!

《C语言》
《C++深度学习》
《Linux》
《数据结构》
《数学建模》
文章目录
- 内存中的程序与操作系统
- 用户态与内核态:权限边界
- 权限识别与切换
- 段描述符表控权
- 捕捉信号:sigaction用法
- sigaction函数结构
- 函数返回值
- sigaction的优势
- 代码示例与运行结果
- sa_mask信号集:屏蔽规则
- 核心机制
- 规则更新说明
- 实际系统表现与扩展屏蔽
- 扩展屏蔽代码示例
内存中的程序与操作系统
程序一运行,它的代码和要用的数据就会“住进”物理内存;操作系统本身也是软件,自然也得占一块内存空间。
每个用户程序(进程)都有自己专属的“地址映射表”(用户页表)——就像每家有自己的门牌号对照表,所以这类表会有很多份;但操作系统的“地址映射表”(内核页表)全系统只需要一份,所有进程共用。
为啥这么设计?很简单:操作系统的代码和数据是固定的,一份映射表就够让所有进程找到它,不管进程怎么切换,都能精准定位到操作系统。
那操作系统的内存里都装了啥?看下面这张图就清楚了:

用户态与内核态:权限边界
用户进程和操作系统(内核)共用0-4GB的“虚拟内存地盘”。这时候你可能会问:用户进程会不会偷偷跑到内核的地盘,乱访问内核的代码和数据?
答案是:绝对不会!因为操作系统对谁都不放心,它用“用户态”和“内核态”划了条严格的权限红线:
- 用户态:进程以普通用户身份运行,只能在自己的0-3GB虚拟内存里折腾;不用麻烦操作系统时,直接用自己的虚拟地址就能操作自己的资源;
- 内核态:进程以内核身份运行(比如要调用系统功能时),能访问整个0-4GB虚拟内存,负责管理电脑的全局资源(比如CPU、内存)。
权限识别与切换
CPU里有个cs寄存器,它的前两个比特位是“权限身份证”(CPL),能判断当前进程的权限:
- 数值0:内核态;
- 数值3:用户态。

如果用户态进程敢碰3-4GB的内核地盘,CPU一检测到越权,就会直接终止这个进程,绝不留情。
那用户态怎么切到内核态?得用专门的“指令钥匙”:
int 0x80:比较老的系统调用方式;syscall:更高效的系统调用方式。
这两个指令的作用很简单:把cs寄存器前两位改成0(切到内核态),再跳转到内核的内存地址——相当于“进入内核的办公区”。

段描述符表控权
除了用户态、内核态的大划分,还有“段描述符表”(分全局、局部两种)来细化权限管理。表里面的RPL和DPL是两个“权限检查官”:
- DPL:某块内存区域(段)本身的权限级别,规定了访问它至少需要什么权限;
- RPL:请求访问该区域的进程的权限级别。内核会对比这两个级别,只有满足要求,才允许进程访问。
捕捉信号:sigaction用法
在Linux里,除了signal函数能处理信号,sigaction是更灵活的“信号处理器”——既能查看当前信号的处理方式,也能修改它的处理动作。
sigaction函数结构
这张图把函数的“长相”和参数分工讲得很清楚:

signum:要操作的信号编号(比如SIGINT,就是按Ctrl+C触发的信号);act:指向sigaction结构体的指针,里面装着新的信号处理配置(比如用哪个函数处理信号);oldact:用来保存原来的信号处理配置,不用保存的话填NULL就行。
函数返回值
- 成功:返回0;
- 失败:返回-1,还会设置errno变量,告诉你哪里出错了。

sigaction的优势
它比signal函数功能更全——不仅能处理普通信号,还能处理实时信号,应对复杂的信号场景更给力。

代码示例与运行结果
下面是实际使用代码,注释写得很细,跟着看就能懂:

比如代码里定义了sig_handler函数处理信号,再用sigaction把SIGINT信号和这个函数绑定;运行后按Ctrl+C,就能看到信号被成功捕捉,输出对应的提示——运行效果如下:

sa_mask信号集:屏蔽规则
sigaction结构体里有个sa_mask成员,它是“信号屏蔽清单”——负责控制信号处理期间,哪些信号不能进来打扰。
核心机制
当某个信号的处理函数开始工作时,内核会自动把这个信号加到“屏蔽表”(block表)里,把对应的位从0改成1——防止同一个信号反复来捣乱;等处理函数执行完返回,再把这个信号从屏蔽表里去掉,位从1改回0。
还有个“未决表”(pending),状态和屏蔽表正好相反:信号没处理时位是1,处理完就变成0。

说白了:操作系统不允许同一个信号同时被多次处理,得一个一个来。
规则更新说明
关于屏蔽表和未决表的状态变化,看这张图就能理清:

实际系统表现与扩展屏蔽
在Ubuntu 20.04、CentOS 7这些常见系统里,当前正在处理的信号会被自动屏蔽。如果想在处理某个信号时,顺便屏蔽其他信号,把那些信号加到sa_mask里就行。



扩展屏蔽代码示例
下面的代码演示了怎么给sa_mask加额外的屏蔽信号——比如处理SIGINT时,顺便屏蔽SIGQUIT(按Ctrl+\触发的信号),这样在处理SIGINT期间按Ctrl+\,信号会被屏蔽,等SIGINT处理完才会生效:

