Linux信号处理、中断、与页表映射

信号产生

使用终端按键产生信号

Ctrl-c, Ctrl-z, Ctrl-\都是常用的信号,但是他们只能作用于前台进程

一个bash中,只能有一个前台进程,但是可以有若干个后台进程

使用命令

cpp 复制代码
kill -n pid_t

使用函数

  1. kill
cpp 复制代码
int kill(pid_t pid, int sig);

向指定进程发送信号

  1. raise
cpp 复制代码
int raise(int sig);

用于向进程自己发送信号

软件条件满足时发送信号

alarm函数

cpp 复制代码
unsigned int alarm(unsigned int seconds);

一个计时器,当时间结束后,会发送SIGALRM信号终止程序

coredump

  1. 默认不开启,使用ulimit -c size设置允许的coredump文件大小,当程序出现段错误时,会产生coredump文件,以方便调试
  2. 自定义信号捕捉
cpp 复制代码
typedef void(*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

自定义捕捉信号指示定义了如何处理,并不等于处理动作

硬件异常

当硬件运行出现错误时,比如除零错误,会触发SIGFPE信号;访问野指针,会触发SIGSEGV信号

cpp 复制代码
void handler(int signo)
{
  prinrf("received: %d\n", signo);
}
signal(SIGFPE, handler);
int a = 10;
a /= 0;

使用signal自定义信号捕捉之后,发现一直在循环打印自定义捕捉内容,原因是在cpu中存在控制和状态寄存器,可以理解其起到位图的作用,记录产生的异常信号,

当发生异常信号,执行完我们的处理函数之后,并没有发生cpu上下文的切换,寄存器中仍然保存着出错误的信息,所以会一直打印接收到的信号

信号的保存

  1. 信号状态
  • 信号递达,信号实际进行处理的状态
  • 信号未决,信号产生到信号递达这中间的状态叫做信号未决
  • 信号阻塞和信号忽略,信号阻塞是进程可选的,只有进程先解决信号阻塞状态到信号未决状态,才能有机会进入信号递达状态;信号忽略是在信号递达之后,进程可选的一种信号处理方式
  1. 信号在内核中的表示

    在task_struct中,存放着三个表,分别是handler表、pending表、block表,handler中保存着函数处理方式的函数指针,如果有自定义就指向自定义的处理函数;
    pending表是未决信号的记录表,当信号产生时,会从0被置为1;block记录的是是否阻塞这个信号,之后pending表为1并且block相应记录为0时,才能够进入信号递达状态

  2. 普通信号,对于多次接受的情况,只记录一次;但是对于实时信号,会通过队列的方式多次记录

信号集

  1. 前面提到的pending表和block表,结构一样,都是用1、0表示一个信号是否是当前自己的有效信号。我们用sigset_t来表示当前信号的有效情况,例如pending表的
    sigset_t表示是否处于未决状态,而block表的sigset_t表示是否处于阻塞状态。sigset_t的信息、操作都要通过函数进行,直接操作例如使用printf打印是没有意义的
  2. 初始化信号集
cpp 复制代码
int sigemptyset(sigset_t* sig);
int sigfillset(sigset_t* sig);

信号集要先初始化在使用,emptyset将其初始化为空集,而fillset会根据当前进程信号集的有效情况进行初始化

  1. 增减有效元素
cpp 复制代码
int sigaddset(sigset_t* sig, int signum);
int sigdelset(sigset_t* sig, int signum);

通过这两个接口去增加后者删除信号集中的有效信号

  1. 获取未决信号集
    通过一个输出型参数带出信息
cpp 复制代码
int sigpending(sigset_t* sig);
  1. 获取或者修改当前进程的block或者pending信号集
cpp 复制代码
int sigprocmask(int how, const sigset_t* set, sigset_t* oset)

当oset不为空时,将当前进程的信号集信息传入到oset中带出;当set不为空时,将当前进程信号集设置为oset的描述;若两者均不为空,则将执行前面两个过程,既拷贝带出,也进行当前进程信号集设置

信号捕捉

  1. 执行主控制流程的某条指令时,因为异常、中断、系统调用等原因,从用户态进入内核态
  2. 执行完内核态中的处理流程时,检查进程的pending表,如果有能够处理的信号,操作系统会再次进入用户态。不过这次不是进入主控制流程,而是去执行用户自定义
    的处理函数(如果没有就直接进行处理,然后返回主控制流程的上下文,继续往下执行),这里并不是返回了主控制流程,而是另外开辟了新的堆栈,去执行用户的自定义信号处理
    处理完成后,返回内核态,随后返回用户态,从主控制流程之前的状态往下运行

中断

硬件中断

当硬件设备就绪时(比如移动鼠标、按动键盘),会向中断控制器发送中断。随后,中断控制器会通知cpu,cpu随后向中断器发送信号,获取中断号。随后cpu会将当前进程在寄存器中的上下文

拷贝到进程在内存的相应空间之中,这一过程称作cpu保护现场。之后,操作系统会根据拿到的中断号去查阅中断向量表,中断向量表中记录着对于各种硬件、软中断等等的中断服务

所需进行的处理。完成相应处理函数之后,操作系统返回用户态,恢复cpu现场,继续往下执行被中断的流程

时钟中断

上面理解了操作系统如何既进行用户代码的处理也处理硬件中断,那进程调度呢?现代cpu中继承了一个时钟源,其每个一段时间就会向cpu中发送时钟中断。cpu获得

时钟中断之后,就会到中断向量表中去执行时钟中断进程调度的处理,从而完成进程的切换。

这里明白了,其实所谓进程的时间片,就是一个计时器

软中断

为了让操作系统支持系统调用,cpu设计了syscall和int 0x80这两个汇编指令,当这两条指令被执行时,cpu会触发软中断,去根据中断号查阅系统调用表,完成相应的处理

缺页中断、内存碎片处理、除零野指针错误,这些都会通过软中断,通知操作系统进行处理

陷阱和异常

cpu中的软中断,比如syscall和int 0x80,这些被称作陷阱;除零错误、野指针,这些被称作异常

内核态和用户态

在进程的地址空间当中[0,3]GB是用户的代码(代码段、数据段、堆、栈、共享区等),[3, 4]GB是内核的代码。运行用户的代码时,就是用户态;运行内核的代码时,就是内核态

页表

如同国家、省、市、区、街道、小区、家这样从大到小,一层一层检索出具体地址的过程,从虚拟地址到物理地址的映射也是类似进行的。

对于64位机器,高16位不用来查询页表,低48位被分成五部分使用。

前三个高九位,分别用来查询三级页目录,接下来根据再低9位查询叶子页表。这就是高36位

虚拟地址和物理地址都以4kb为单位进行划分,虚拟内存的叫做分页,物理内存的叫做粉叶框,一个字节在页和叶框中的偏移量是相同的。

接着上面剩下的最后低12位虚拟地址,这里正好和4kb的分页一个字节一个字节的对应,就通过这些位去查询叶子页表,获得虚拟地址到物理地址的具体映射

如果没有找到页表中想要查询的字节,就说明相应内容还没有被加载到物理内存当中,这时就会出发缺页中断,cpu由用户态转入内核态,完成从硬盘加载相应内容的操作

cpu中,mmu负责完成这一过程,其中有一块高速缓存------块表(tlb),里面存放着最近用过的映射

相关推荐
小白同学_C6 小时前
Lab4-Lab: traps && MIT6.1810操作系统工程【持续更新】 _
linux·c/c++·操作系统os
今天只学一颗糖6 小时前
1、《深入理解计算机系统》--计算机系统介绍
linux·笔记·学习·系统架构
2601_949146536 小时前
Shell语音通知接口使用指南:运维自动化中的语音告警集成方案
运维·自动化
儒雅的晴天6 小时前
大模型幻觉问题
运维·服务器
Gofarlic_OMS7 小时前
科学计算领域MATLAB许可证管理工具对比推荐
运维·开发语言·算法·matlab·自动化
通信大师7 小时前
深度解析PCC策略计费控制:核心网产品与应用价值
运维·服务器·网络·5g
dixiuapp8 小时前
智能工单系统如何选,实现自动化与预测性维护
运维·自动化
不做无法实现的梦~8 小时前
ros2实现路径规划---nav2部分
linux·stm32·嵌入式硬件·机器人·自动驾驶
Elastic 中国社区官方博客8 小时前
如何防御你的 RAG 系统免受上下文投毒攻击
大数据·运维·人工智能·elasticsearch·搜索引擎·ai·全文检索
小锋学长生活大爆炸8 小时前
【教程】免Root在Termux上安装Docker
运维·docker·容器