操作系统的心脏节拍:CPU中断如何驱动内核运转?

目录

一、硬件中断

二、时钟中断

三、软中断

四、用户态与内核态


一、硬件中断

为引出今天的话题,我们来思考这样一个问题:

cpp 复制代码
#include<stdio.h>
int main()
{
    int a;
    scanf("%d",&a);
    return 0;
}

当以上程序执行到scanf时,如果我们不输入信息,程序会一直阻塞在这里。那程序如何知道我们已经完成输入了呢?而且我们输入的信息在外设上,程序又是如何知道何时将其载入内存的呢?

**这其实是由外设就绪后通知给CPU,再由CPU通知给操作系统,然后由操作系统调用最后通知给前台程序。**具体细节如下:

每个中断都有自己的编号,即中断号 ,在中断控制器中会有寄存器来记录已就绪的中断号。一旦有外设就绪,它的中断号将被存入寄存器,然后由中断控制器通知CPU。CPU得到信息后会从中断控制器的寄存器中获取中断号,并停止当前工作,保持好当前数据(保存上下文 ),然后通知操作系统执行对应的中断方法。处理完后,CPU恢复现场,继续之前的工作。

如上图所标记的执行顺序:1,2,3,4,5,6,7。

我们都知道CPU运行速度非常快,外设的数据不能直接与CPU打交道,而是要通过存储器作为桥梁。注意这只是说外设的数据不与CPU打交道,不代表外设就不与CPU打交道,如下冯·诺伊曼体系结构:

OS(操作系统)中储存了各种处理中断的不同方法,通过上面的执行逻辑我们可以知道,OS并不需要关心外设是否就绪,只需要等待CPU通知即可。

像这样由硬件产生的中断叫硬件中断,同理就有软件中断。下文会细讲。

二、时钟中断

我们回顾一下,操作系统是一个对软硬件进行管理的软件,它的工作通常包括:进程调度,为程序提供系统调用。

其实操作系统做的所有工作都是一种中断处理。

那么当没有中断需要处理时,操作系统在做什么?

这个时候操作系统什么也不干,就暂停在那里。对,你没听错,操作系统也是牛马啊!CPU没给它派发中断任务,它就只管闲着就好。
OS做的任何工作都是对中断的处理,那么OS进行进程调度也是收到中断信号吗?

是的!除了以上讲的外设会生成中断信号以外,还有一个特殊的设备:"时钟源",它被集成在CPU内部,会以特定的频率向CPU发送中断,这也就是时钟中断。

OS在接收到时钟中断信号后,更新当前进程的时间片,并检查时间片是否耗尽(即计数器是否为0)。如果已经耗尽,则进行进程调度,如果没有,则无需任何操作。

注意:时钟中断的频率是固定的,而进程的时间片是根据具体情况动态调整的。

总结:操作系统是基于中断机制进行工作的软件。

三、软中断

除了硬件产生中断以外,在程序中写的系统调用也会产生中断,我们把这种由软件触发的中断叫作软中断。

也就是说CPU也可以主动的触发中断,并不一定是由硬件触发。

缺⻚中断?内存碎⽚处理?除零野指针错误?这些问题,全部都会被转换成为CPU内部的软中断,然后执行中断处理例程,完成所有处理。有的是进⾏申请内存,填充⻚表,进⾏映射的。有的是⽤来处理内存碎⽚的,有的是⽤来给⽬标进程发送信号,杀掉进程等等。

又比如在处理系统调用产生的中断方法中,有着这样一张表:

它实际上是一个函数指针数组,储存了各种系统调用方法。 所以每一个系统调用都有唯一的下标,这个下标也叫作系统调用号。

但我们会发现这些函数好像都没用过,然而名字却有些熟悉。确实,我们并不会直接调用这里的函数,而是使用fork,read,write,open等。也就是说我们平常所用的这些函数并非真正意义上的系统调用,而是在系统调用的基础上进行封装的。OS把它做得这么复杂也是为了自我保护。

比如我们用的read函数里面封装了这样的汇编代码:

cpp 复制代码
int 0x80 
move eax 5
  • int 0x80:让CPU产生软中断,也可以通过syscall触发
  • move eax 5:把read的系统调用号5存入寄存器。

通过int 0x80找到这样的中断方法:

cpp 复制代码
void CallSystem()
{
    //获取系统调用号
    int n = 0;
    move n eax
    //... ...
    //系统调用方法
    sys_call_table[n]();

}

四、用户态与内核态

虚拟地址空间分为两大部分,其中1GB是内核区,3GB是用户区。不同的PCB虚拟地址空间的用户页表是不同的,但是它们的内核页表是相同的。如下:

这也说明,无论操作系统怎么切换进程,都能通过内核区找到同一个操作系统。即操作系统调用方法的执行是在进程的地址空间中进行的。

而进程执行的本质也是在地址空间上跳转,那么用户在写程序时不就可以直接去内核地址空间访问内核了吗。

这样的情况确实可以实现,因此为保护操作系统内核安全,就引入了一系列的权限机制。

比如用户态内核态就是一种权限,在CPU中存在一个寄存器CPL标记当前所处的权限,0表示内核态,3表示用户态。

在进行系统调用时就需要进行权限的更改,比如int 80或syscall在触发中断时也进行权限的更改,使其陷入内核。

非常感谢您能耐心读完这篇文章。倘若您从中有所收获,还望多多支持呀!

相关推荐
m0_7385963215 分钟前
十大排序算法
算法·排序算法
jingfeng51417 分钟前
详解快排的四种方式
数据结构·算法·排序算法
MoRanzhi120332 分钟前
245. 2019年蓝桥杯国赛 - 数正方形(困难)- 递推
python·算法·蓝桥杯·国赛·递推·2019
什么半岛铁盒39 分钟前
【Linux系统】Linux环境变量:系统配置的隐形指挥官
linux
henyaoyuancc1 小时前
vla学习 富
人工智能·算法
atbigapp.com1 小时前
9个数据分析指令为工作赋能
人工智能·数据分析·aigc
Lw老王要学习1 小时前
Linux容器篇、第一章_02Rocky9.5 系统下 Docker 的持久化操作与 Dockerfile 指令详解
linux·运维·docker·容器·云计算
橙子小哥的代码世界1 小时前
【大模型RAG】Docker 一键部署 Milvus 完整攻略
linux·docker·大模型·milvus·向量数据库·rag
Gyoku Mint2 小时前
机器学习×第五卷:线性回归入门——她不再模仿,而开始试着理解你
人工智能·python·算法·机器学习·pycharm·回归·线性回归
DisonTangor2 小时前
【字节拥抱开源】字节团队开源视频模型 ContentV: 有限算力下的视频生成模型高效训练
人工智能·开源·aigc