信号的处理时机(内核态,用户态,如何/为什么相互转换,内核空间,cpu寄存器),信号的处理流程详细介绍+抽象图解

目录

信号的处理时机

引入

思考 -- 什么时候才能算合适的时候呢?

用户态转为内核态

引入

内核地址空间 

引入

思考 -- 进程为什么能切换成内核态呢?

虚拟地址空间

注意点

原理 (总结一下)

为什么+如何进入内核态 

引入

介绍

底层原理(int 80) 

CPU的寄存器

状态切换

如何知道进入了内核态

硬件上

软件上

用户态和内核态的来回切换 

为什么要来回切换

如何切换

信号的处理流程 

介绍

详细图解

描述 

默认操作

忽略操作

自定义操作

抽象图解 

交叉点处

方向

横线


信号的处理时机

引入

  • 之前我们说过,信号被os发送到进程,实际上是修改了pending信号集
  • 然后进程会在合适的时候处理

那究竟什么时候才能算合适的时候呢?

思考 -- 什么时候才能算合适的时候呢?

处理信号必然不是我们做的,而是os,因为信号是内核数据

  • 那么处理信号时,就得处于内核的范畴,不然怎么访问os的数据 -- 也就是处于内核态
  • 那么肯定也会存在用户态,和内核态对应
  • 也就存在内核态和用户态的切换
  • 那么内核态就拥有三种阶段 -- 刚进入内核态,在内核态执行任务时,准备转为用户态时

是哪个阶段开始处理信号的呢?

  • 一般是在执行完任务后,内核态准备转为用户态时,会进行信号的处理
  • (因为任务肯定第一位嘛,信号的处理又不急于这一时)

用户态转为内核态

引入

虽然我们是这样说的 -- 由内核态转为用户态时,就会处理信号

  • 但是这个操作我们看不到,怎么知道到底有没有转换呢?
  • 说到底,究竟是怎么转换的呢?未免有点太抽象了

内核地址空间 

引入

其实并不抽象,所有看起来复杂的行为都是将[底层一些简单的操作]封装而成的

  • 比如,可以首先来思考这个问题 -- 进程为什么能切换成内核态呢?
思考 -- 进程为什么能切换成内核态呢?

内核态,也就是可以使用os内部的数据/接口

为什么一个进程,它可以有这样的行为呢?

  • 肯定是它此时的权限修改成了os的权限

如何使用os内部的数据/接口呢?

  • 要使用这些,必然是和使用普通数据/接口一样,访问实际的内存
  • 毕竟他们本质没有区别,只是权限不一样
  • 而访问内存必须通过虚拟地址空间+页表+MMU实现
  • 但我们无法直接操作物理内存,既然可以访问内核数据,那内核肯定也有自己的进程空间+页表

那到底有没有呢?

  • 有的
虚拟地址空间

之前学习的地址空间中本身就有1G的空间,是存放内核空间的: 

  • 也就是说,我们切换为内核态,依赖的是 -- 进程地址空间有内核空间
注意点

既然有内核空间,自然也有内核级的页表用来映射

但是要注意!!!

  • 每个进程的用户空间,对应的是独立的内存块

难道每个进程拥有的内核空间,它对应的内存也是独立的吗?

  • 不会的!
  • 内核资源是所有进程共用的,不会让每个进程去瓜分一部分
  • [进程的独立性]要求用户空间对应的内存都是独立的,但每个进程可以使用相同的os资源
  • 所以,内核级的页表只需要一份(对应的物理内存也是同一块),它可以被所有进程看到

所以,无论进程如何切换,我们看到的都是同一份os资源

原理 (总结一下)

进程可以切换为内核态的原因就在于,每个进程中都有内核地址空间

  • 只要切换访问位置和访问权限,自然可以通过内核地址空间访问内核数据噜

为什么+如何进入内核态 

引入

知道了进程是可以切换的

  • 那么究竟为什么要切换成内核态,底层又是如何切换的呢?
介绍

当我们需要访问内核数据时,就需要进入内核态

  • 我们大多都是通过系统调用接口,来使用内核数据的
  • 所以,调用系统调用,自动就可以进入内核态(因为之前在使用的时候,并没有什么特殊行为)

并且,我们不用把系统调用想的那么神秘,他也只是一个函数而已(只不过非常接近底层)

  • 系统调用和使用库函数本质上没有区别,都是在进程自己的地址空间内进行跳转

底层原理(int 80) 

在每个系统调用函数开始前,都需要先切换成内核态

  • 它通过int(interrupt 的缩写) 80h 这个汇编指令,来切换成内核态
  • 汇编的底层是通过CPU的寄存器权限相关字段实现的
CPU的寄存器

虽然存在两种状态(内核态,用户态),但执行代码时使用的是同一套寄存器

  • 当CPU在用户态执行程序时,寄存器用于存储用户程序的状态和数据
  • 当切换到内核态时,寄存器则用于内核代码的执行

切换过程中,内核会保存用户态的寄存器状态(也就是保存上下文数据),以便在返回到用户态时恢复,并恢复内核态的上下文;反过来也一样

  • 因此,虽然使用的是相同的寄存器,但内核态和用户态的操作和数据是相互独立的
状态切换

int 80h这个指令涉及多个字段和寄存器的修改,比如:

  • CR0-CR3寄存器,段寄存器,RPL、CPL、DPL,段选择子等等

如何知道进入了内核态

硬件上

通过读取寄存器的状态(寄存器是硬件的一部分)

软件上

其实我们已经在上面介绍了,只要检测执行的位置是否指向内核空间即可

  • 指向内核空间,就说明此时正在执行系统级别的代码,此时也就处于内核态

用户态和内核态的来回切换 

为什么要来回切换

总不能从用户态切到内核态后,我们就不切回来了吧

我们肯定还有很多没有执行的用户级代码呢

如何切换

其实原理我们已经都介绍过了

  • 只要我们可以拿到进程执行相关的数据,我们也就可以按照上面的原理 -- 跳转地址空间,修改寄存器和相关字段数值,来切换状态
  • (当然这里只是一个大概,实际上非常复杂的)
  • 刚好cpu的寄存器里面,就存放着很多进程执行相关的数据结构的指针(也就是进程的上下文),可以用来恢复进程,从上次执行的位置继续向下执行

信号的处理流程 

知道了原理后,我们就可以明白,os究竟是如何处理信号的

介绍

详细图解

描述 

首先我们要先陷入内核态(可能是系统调用,也可能是异常/中断导致的)

  • 然后在内核态下,os完成一系列的任务
  • 在即将返回用户态,继续向下执行代码前,我们正好处于内核态下,有能力,可以顺手处理收到的信号

所以,os开始处理信号

  • os先去检查信号在内核中的两种信号集 -- pending,block
  • 如果该信号被阻塞,os可以直接返回到用户态
  • 如果满足条件,我们拿到对应信号的处理函数后 ...

这里就要分情况讨论了:

默认操作
  • 如果需要终止进程,os可以直接进行终止进程的调度
  • 如果该进程中有需要刷新数据/dump到外设,os在内核态下可以直接操作,操作完再退出进程
  • 如果该进程不退出,也是一样的:
  • eg:有个信号用来暂停进程,也可以在内核态下直接实现,直接修改进程状态,然后调度下一个进程
忽略操作
  • 如果是忽略,os只需要将pending信号集由1改为0,即可返回到用户态,执行下面的代码噜
自定义操作
  • 如果是自定义的操作,就有点麻烦了
  • 因为默认和忽略都可以在内核态下直接完成,因为os的权限是最大的,什么资源都可以拿到
  • 但是在执行自定义函数时,需要转换到用户态才行

为什么要转换到用户态呢?内核态下不能执行吗?

  • 内核态当然可以执行进程的代码,它可以拿到所有的资源
  • 但是,万一该函数中有违规操作(用户态下不可以执行的操作)
  • 却没有被内核态下的进程识别到,就可能会导致不好的后果
  • 为了避免这些情况,普通代码还是让用户态进程执行的好

在用户态下执行完成后,也就该返回内核态

  • 不仅是因为,处理完信号后,需要修改pending位图
  • 也在于,进程需要返回到陷入内核态的执行位置,然后继续向下执行
  • 而这些操作都需要访问os资源

抽象图解 

信号处理过程 -- 从信号需要从用户态陷入到内核态 -> 处理完信号,返回原先的执行位置 抽象出来,得到该图:

交叉点处

就是os开始处理信号的时候

方向

图中的箭头,就是os执行流的变化方向

横线
  • 如果用一条横线贯穿该图,那么横线上方就是用户态,下方就是内核态
  • (可以看到信号处理的时候是在内核态中)
  • 横线与图的四个交点就可以代表,进程的状态变化次数
  • 交点所在的方向,就代表了状态的切换方向
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值