手写简易操作系统(十)--中断概述

前情提要

我们还是总结一下前面做了什么

1、计算机启动,BIOS将MBR导入到内存,并跳转到相应位置执行

2、MBR将Loader导入到内存,并跳转执行

3、Loader中开启保护模式,准备好GDT表,开启内存分页,从此之后CS中保存的是GDT的选择子,生成的线性地址是虚拟地址了,需要经过页部件的转换才能找到真实的物理地址。Loader将内核导入到内存的 0xc0001500 地址处。

4、Loader跳转到内核执行

上一节,我们在内核里面实现了内核的打印函数,这一节我们讲中断,操作系统中有一个名言就是 操作系统是中断驱动的

一、什么是中断

在计算机系统中,中断(Interrupt)是一种硬件或软件生成的信号,用于通知处理器某种事件已发生,需要处理器暂时中断当前执行的程序或任务,转而执行相应的中断服务程序(Interrupt Service Routine,ISR)。

当处理器接收到中断信号时,会立即停止当前正在执行的程序,保存当前的执行状态(比如程序计数器、寄存器状态等),然后跳转到对应的中断服务程序开始执行。中断服务程序会处理中断引起的事件,并在处理完成后恢复原来的执行状态,使被中断的程序继续执行。

中断机制可以提高系统的实时性和响应能力,允许处理器及时响应外部事件或异常情况,从而更有效地管理系统资源和处理各种任务。

二、中断分类

中断就是发生了某种事件需要通知CPU处理。所以,不管哪里,只要有事发生就应该让CPU知道。把中断按事件来源分类,来自CPU外部的中断就称为外部中断,来自CPU内部的中断称为内部中断。还可以再细分,外部中断按是否导致宕机来划分,可分为可屏蔽中断和不可屏蔽中断两种,而内部中断按中断是否正常来划分,可分为软中断和异常。

2.1、外部中断

CPU有两根线接收外部中断信号,一个是INTR可屏蔽中断,接收正常的外设中断,一个是NMI不可屏蔽中断,接收一些会导致机器宕机的灾难性错误。只要是从NMI接收的中断,基本全是硬伤,从INTR接收的中断,CPU甚至可以装作不知道。

image-20240315154732211

操作系统是中断驱动的,当发生中断时会执行相应的中断处理程序,我们希望操作系统响应中断的时间越短越好,这样的话可以腾出时间响应更多的中断,但是中断是要完整执行的,所以Linux中将中断处理程序分为上下两个部分。把中断处理程序中需要立即执行的部分(分分钟不能耽误的部分)划分到上半部,这部分是要限时执行的,而中断处理程序中那些不紧急的部分则被推迟到下半部中去完成。

CPU收到中断后,得知道发生了什么事情才能执行相应的处理办法。这是通过中断向量表或中断描述符表来实现的,首先为每一种中断分配一个中断向量号,中断向量号就是一个整数,它就是中断向量表或中断描述符表中的索引下标,用来索引中断项。中断发起时,相应的中断向量号通过NMI或INTR引脚被传入CPU,中断向量号是中断向量表或中断描述符表里中断项的下标,CPU根据此中断向量号在中断向量表或中断描述符表中检索对应的中断处理程序并去执行。

2.2、内部中断

内部中断可以分为软中断和异常,软中断,就是由软件主动发起的中断,因为它来自于软件,所以称之为软中断。异常是指令执行期间CPU内部产生的错误引起的。

2.2.1、软中断

首先看软中断,下面这些是可以发起中断的指令

int 8位立即数 我们以后常用的指令,我们要通过它进行系统调用,256也就是系统支持的中断数

int3 调试断点指令,其所触发的中断向量号是3

into 中断溢出指令,它所触发的中断向量号是4,前提是eflags寄存器中OF位为1才能触发

bound 检查数组索引越界指令,其所触发的中断向量号是5

ud2 未定义指令,其所触发的中断向量号是6

2.2.2、异常

除第一种的“int 8位立即数”之外,其他的几种又可以称为异常。因为它们既具备软中断的“主动”行为,又具备异常的“错误”结果。

下面看异常,异常分为三种

Fault 故障。这种错误是可以被修复的一种类型,属于最轻的一种异常

Trap 陷阱,这一名称很形象地说明软件掉进了CPU设下的陷阱,用于调试

Abort 终止,从名字上看,这是最严重的异常类型,一旦出现,由于错误无法修复,程序将无法继续运行,操作系统为了自保,只能将此程序从进程表中去掉。

看看计算机会提供的一些中断和异常

image-20240315160144383

第一列式中断或者异常的向量号,后面都是一些描述简写,最后一个是错误码,如果值为Y表示CPU会将此中断的错误码压入栈中。

中断机制的本质是来了一个中断信号后,调用相应的中断处理程序。所以,CPU不管有多少种类型的中断,为了统一中断管理,把来自外部设备、内部指令的各种中断类型统统归结为一种管理方式,即为每个中断信号分配一个整数,用此整数作为中断的ID,而这个整数就是所谓的中断向量,然后用此ID作为中断描述符表中的索引,这样就能找到对应的表项,进而从中找到对应的中断处理程序。

三、中断描述符表IDT

中断描述符表(Interrupt Descriptor Table,IDT)是保护模式下用于存储中断处理程序入口的表,当CPU接收一个中断时,需要用中断向量在此表中检索对应的描述符,在该描述符中找到中断处理程序的起始地址,然后执行中断处理程序。

之前其实我们看过中断描述符表的结构,现在我们再看一下

image-20240315160514436

S 位表示这是数据段还是系统段,之前我们都是设置为数据段,现在这里我们设置为代码段了

p 中断处理程序是否在内存中

DPL 表示访问这个门需要的最小特权级

中断门包含了中断处理程序所在段的段选择子和段内偏移地址。当通过此方式进入中断后,标志寄存器eflags中的IF位自动置0,也就是在进入中断后,自动把中断关闭,避免中断嵌套。Linux就是利用中断门实现的系统调用,就是那个著名的int 0x80。中断门只允许存在于IDT中。

以前说过的低端1MB内存布局,位于地址0~0x3ff的是中断向量表IVT,它是实模式下用于存储中断处理程序入口的表。由于实模式下功能有限,运行机制比较“死板”,所以它的位置是固定的,必须位于最低端。大家看到了,已知0~0x3ff共1024个字节,又知IVT可容纳256个中断向量,所以每个中断向量用4字节描述。

对比中断向量表,中断描述符表有两个区别。

(1)中断描述符表地址不限制,在哪里都可以。

(2)中断描述符表中的每个描述符用8字节描述。

既然IDT的位置不固定,当中断发生时,CPU是如何找到它的呢?其实不难想象,你GDT有个专门的寄存器存着,我IDT就不行嘛?中断描述符表寄存器(Interrupt Descriptor Table Register,IDTR),其结构如下

image-20240315162901908

16位的表界限,表示最大范围是0xffff,即64KB。可容纳的描述符个数是64KB/8=8K=8192个。GDT的第一个描述符是不可用的,但是IDT没有这个限制。处理器只支持256个中断,即0~254,中断描述符中其余的描述符不可用。

32位的表基地址,就是IDT的线性基地址。

加载IDTR也有个专门的指令—lidt,其用法和gdtr一模一样。

四、中断处理过程

具体分为四步

(1)处理器根据中断向量号定位中断门描述符。

中断向量号是中断描述符的索引,当处理器收到一个外部中断向量号后,它用此向量号在中断描述符表中查询对应的中断描述符,然后再去执行该中断描述符中的中断处理程序。

(2)处理器进行特权级检查。

由于中断是通过中断向量号通知到处理器的,中断向量号只是个整数,其中并没有RPL,所以在对由中断引起的特权级转移做特权级检查中,并不涉及到RPL。中断门的特权检查同调用门类似,对于软件主动发起的软中断,当前特权级CPL必须在门描述符DPL和门中目标代码段DPL之间。这是为了防止位于3特权级下的用户程序主动调用某些只为内核服务的例程。

(3)执行中断处理程序。

特权级检查通过后,将门描述符目标代码段选择子加载到代码段寄存器CS中,把门描述符中中断处理程序的偏移地址加载到EIP,开始执行中断处理程序。

image-20240315163815578

整体过程如上图所示。

中断发生后,eflags中的NT位和TF位会被置0。如果中断对应的门描述符是中断门,标志寄存器eflags中的IF位被自动置0,避免中断嵌套。若中断发生时对应的描述符是任务门或陷阱门的话, CPU是不会将IF位清0的。因为陷阱门主要用于调试,它允许CPU响应更高级别的中断,所以允许中断嵌套。而对任务门来说,这是执行一个新任务,任务都应该在开中断的情况下进行,否则就独占CPU资源,操作系统也会由多任务退化成单任务了。

IF位只能限制外部设备的中断,对那些影响系统正常运行的中断都无效,如异常exception,软中断,如int n等,不可屏蔽中断NMI都不受IF限制。

NT位表示Nest Task Flag,即任务嵌套标志位,也就是用来标记任务嵌套调用的情况。任务嵌套调用是指CPU将当前正执行的旧任务挂起,转去执行另外的新任务,待新任务执行完后,CPU再回到旧任务继续执行。为什么CPU执行完旧任务后还能回到新任务呢?原因是在执行新任务之前,CPU做了两件准备工作。(1)将旧任务TSS选择子写到了新任务TSS中的“上一个任务TSS的指针”字段中。(2)将新任务标志寄存器eflags中的NT位置1,表示新任务之所以能够执行,是因为有别的任务调用了它。这个别的任务就是“上一个任务TSS的指针”指向的任务。

CPU把新任务执行完后还是要回去继续执行旧任务的,怎样回到旧任务呢?这也是通过iret指令。iret指令因此有了两个功能,一是从中断返回,另外一个就是返回到调用自己执行的那个旧任务,这也相当于执行一个任务。那么问题来了,对同一条iret指令,CPU是如何知道该从中断返回呢,还是返回到旧任务继续执行呢?这就用到NT位了,当CPU执行iret时,它会去检查NT位的值,如果NT位为1,这说明当前任务是被嵌套执行的,因此会从自己TSS中“上一个任务TSS的指针”字段中获取旧任务,然后去执行该任务。如果NT位的值为0,这表示当前是在中断处理环境下,于是就执行正常的中断退出流程。

4.1、修改eflags

修改eflags需要压栈在出栈,这样无法保证数据的一致性,而且由于有内存的参与,效率很低,所以,处理器提供了专门用于控制 IF位的指令,通过它,IF可以直接控制。指令cli使IF位为0,这称为关中断,指令sti使IF位为1,这称为开中断。

4.2、中断处理过程中的压栈

(1)处理器根据中断描述符拿到相应的中断描述符后,就找到了中断处理程序对应的选择子,也就找到了即将跳转的代码的DPL,将当前的CPL与DPL做对比,如果当前CPL的特权级比较低,那么就涉及到低特权级向高特权级的转移,必须保存好旧栈的 SSESP 的值,这两个值记为 SS_oldESP_old,然后在TSS中找到同目标代码段DPL级别相同的栈加载到寄存器SSESP中, 记作SS_newESP_new,再将之前临时保存的SS_oldESP_old压入新栈备份,以备返回时重新加载到栈段寄存器SS和栈指针ESPSS 为16为数据,为了对齐会被补充到32位。

(2)在新栈中压入EFLAGS寄存器。

(3)由于要切换到目标代码段,对于这种段间转移,要将CSEIP保存到当前栈中备份,记作CS_oldEIP_old,以便中断程序执行结束后能恢复到被中断的进程。同样,CS寄存器会被对齐到32位。

(4)某些异常会有错误码,记作ERROR_CODE。

如果在第1步中判断未涉及到特权级转移,便不会到TSS中寻找新栈,而是继续使用当前旧栈,因此也谈不上恢复旧栈,此时中断发生时栈中数据不包括SS_old和ESP_old。比如中断发生时当前正在运行的是内核程序,这是0特权级到0特权级,无特权级变化

所以按照顺序压入的是

SS_old -> ESP_old -> EFLAGS -> CS_old -> EIP_old -> ERROR_CODE

处理器进入中断执行完中断处理程序后,还要返回到被中断的进程,这是进入中断的逆过程。中断返回是用iret指令实现的。依次弹出 EIPCSEFLAGS,如果这个时候CPU发现CPL发生了变化,那么还会弹出栈寄存器。

注意,如果在返回时需要改变特权级,将会检查数据段寄存器DS、ES、FS和GS的内容,如果在它们之中,某个寄存器中选择子所指向的数据段描述符的DPL权限比返回后的CPL(CS.RPL)高,即数值上返回后的CPL>数据段描述符的DPL,处理器将把数值0填充到相应的段寄存器。原因在介绍调用门时说过啦,原理是选择子为0便指向GDT中第0个段描述符,该段描述符不可用,从而故意使处理器抛异常。

4.3、中断错误码

上面讲了中断发生时会压入一个中断错误码,错误码格式如下

image-20240315170425706

错误码本质上就是个描述符选择子,通过低3位属性来修饰此选择子指向是哪个表中的哪个描述符。

EXT表示EXTernal event,即外部事件,用来指明中断源是否来自处理器外部,如果中断源是不可屏蔽中断NMI或外部设备,EXT为1,否则为0。

IDT表示选择子是否指向中断描述符表IDT,IDT位为1,则表示此选择子指向中断描述符表,否则指向全局描述符表GDT或局部描述符表LDT。

TI和选择子中TI是一个意思,为0时用来指明选择子是从GDT中检索描述符,为1时是从LDT中检索描述符。当然,只有在IDT位为0时TI位才有意义。

选择子高13位索引就是选择子中用来在表中索引描述符用的下标。

有时候不仅错误码的高16位全为0,低16位也全为0,那一个全0的错误码能指明什么信息?当全0的错误码出现时,表示中断的发生与特定的段无关,或者引用了一个空描述符,引用描述符就是往段寄存器中加载选择子的时候,处理器发现选择子指向的描述符是空的。

中断返回时,iret指令并不会把错误码从栈中弹出,所以在中断处理程序中需要手动用栈指针跨过错误码或将其弹出。否则栈顶处若不是EIP(EIP_old)的话,iret返回时将会载入错误的值到后续寄存器。

通常能够压入错误码的中断属于中断向量号在0~32之内的异常,而外部中断(中断向量号在32~255之间)和int 软中断并不会产生错误码。通常我们并不用处理错误码。

结束语

这一节又全是理论,没有代码,但是我个人认为这一节的理论还是比较重要的,他涉及到了我们在中断时的压栈和出栈,以及我们是怎么处理中断的。整个操作系统就是建立在中断之上的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/754010.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

9成省份“鸿蒙化”,它真起来了?

自去年9月华为宣布鸿蒙原生应用全面启动以来,鸿蒙正以不可阻挡之势,快速在全国千行百业的移动应用领域推进。不仅有支付宝、快手、淘宝、京东等超200家头部互联网企业加入鸿蒙生态;2024年以来,上海、浙江、广西等多省市政务民生、…

C++_day6

思维导图: 2试编程 封装一个动物的基类,类中有私有成员: 姓名,颜色,指针成员年纪 再封装一个狗这样类,共有继承于动物类,自己拓展的私有成员有:指针成员:腿的个数(整型 int count),共有成员函数…

2024全新红娘交友系统定制版源码 | 相亲交友小程序源码 全开源可二开

内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 全新红娘交友系统定制版源码 | 相亲交友小程序源码 全开源可二开 定制版红娘交友平台小程序源码,很牛逼的东西,虽然是小程序,但是有700多M大&…

modbus客户端

通信方式支持 串口 / udp / tcp通信; 设备协议支持RTU / ASCII / TCP; 读取类型支持bool / short / int / float / double / long / batchbool / batchword

JAVA八股day1

遇到的问题 相比于包装类型(对象类型), 基本数据类型占用的空间往往非常小为什么说是几乎所有对象实例都存在于堆中呢?静态变量和成员变量、成员变量和局部变量的区别为什么浮点数运算的时候会有精度丢失的风险?如何解…

【Chapter1】操作系统概述,计算机操作系统教程,第四版,左万利,王英

文章目录 一、操作系统的基本概念1.1操作系统的层次结构1.2操作系统的运行视图1.3操作系统的概念(定义)1.4操作系统的功能和目标1.4.1操作系统的功能和目标——作为系统资源的管理者1.4.2操作系统的功能和目标——向上层提供方便易用的服务1.4.2.1GUI:图形化用户接口…

python 基础知识点(蓝桥杯python科目个人复习计划65)

今日复习内容:做题 例题1:遥远的雪国列车 问题描述: 小蓝和小红今天在房间里一起看完了“雪国列车”这部电影,看完之后他们感触颇深,同时他们想到了这样一道题目: 现在有一个数轴,长度为N&a…

PyTorch学习笔记之激活函数篇(二)

文章目录 2、Tanh函数2.1 公式2.2 对应的图像2.3 对应生成图像代码2.4 优点与不足2.5 torch.tanh()函数 2、Tanh函数 2.1 公式 Tanh函数的公式: f ( x ) e x − e − x e x e − x f(x)\frac{e^x-e^{-x}}{e^xe^{-x}} f(x)exe−xex−e−x​ Tanh函数的导函数&am…

在Latex中优雅的插入svg图片(Ubuntu22.04)

文章目录 一、前言二、准备工作三、脚本编程四、结论 一、前言 在 LaTeX \LaTeX LATE​X 中,插入图片常用的为 figure 环境加 \includegraphics 命令: \begin{figure}[!htbp]\centering\includegraphics[width\textwidth]{图片名.jpg/jpeg/png/pdf}\c…

CSS 零基础入门教程

目录 1. div 和 span2. 什么是CSS?3. CSS 引入方式3.1 内部样式表3.2 外部样式表3.3 行内样式 4. 选择器4.1 标签选择器4.2 类选择器4.3 id 选择器4.4 通配符选择器 5. CSS 基础属性6. 谷歌浏览器调试工具 正文开始。 1. div 和 span 在学习 CSS 之前,…

什么是网站?为什么要搭建网站?

网站:简单来说,网站就是通过互联网来展示信息的页面集合。它可以在电脑或者手机上打开,提供各种功能,比如查看新闻、购买商品、搜索信息等。 一、建网站的目的:展示个人或企业的存在 网站建设的首要目的之一是展示个人…

【C++】三大特性之多态

1 定义及实现 1.1 概念 多态是C三大特性之一。通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。 多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如学…

Java后端面试:框架篇高频面试(Spring、SpringMVC、SpringBoot、MyBatis)

👨‍🎓作者简介:一位大四、研0学生,正在努力准备大四暑假的实习 🌌上期文章:Java后端面试:MySQL面试篇(底层事务、SQL调优) 📚订阅专栏:Java后端面…

十四、ReadWriteLock

ReadWriteLock 读写锁 又叫排他锁 如果使用互斥锁,一个线程在读,其他线程也不能读也不能写 换成读写锁的时候,读线程是读锁,写线程是写锁,写锁是排他的 在多线程大大提高效率,当一个线程在读的时候&…

glib交叉编译

Glib交叉编译 逸一时,误一世。 —— 田所浩二「夏夜银梦」 交叉编译 GLib 涉及到在一个平台上生成能够在另一个平台上运行的目标文件。在这种情况下,我们将会在一台主机(通常是开发机器)上使用交叉编译工具链来构建 GLib 库&#…

从历年315曝光案例,看APP隐私合规安全

更多网络安全干货内容:点此获取 ——————— 随着移动互联网新兴技术的发展与普及,移动APP的应用渗透到人们的衣食住行方方面面,衍生出各类消费场景的同时,也带来了无数的个人隐私数据泄露、网络诈骗事件。 历年来&#xff…

机器人在果园内行巡检仿真

文章目录 创建工作空间仿真果园场景搭建小车模型搭建将机器人放在仿真世界中创建工作空间 mkdir -p ~/catkin_ws/src cd ~/catkin_ws仿真果园场景搭建 cd ~/catkin_ws/src git clone https://gitcode.com/clearpathrobotics/cpr_gazebo.git小车模型搭建 DiffBot是一种具有两个…

Vmware虚拟机配置虚拟网卡

背景 今天同事咨询了我一个关于虚拟机的问题,关于内网用Vmware安装的虚拟机,无法通过本机访问虚拟上的Jenkins的服务。   验证多次后发现有如下几方面问题。 Jenkins程序包和JDK版本不兼容(JDK1.8对应Jenkins不要超过2.3.57)虚…

信号量——生产消费者模型

前文 在这一篇博客(信号量博客)中我曾经提及过信号量的知识,而当对信号量进行提炼总结时,大致是以下三点: 1. 信号量本质是一个计数器(代表资源的数量) 2. 申请信号量本质就是对资源的一种预定机…

final关键字

final关键字 基本介绍final使用细节 基本介绍 final 中文意思:最后的,最终的。 final 可以修饰类、属性、方法和局部变量。 在某些情况下会使用到final: 1) 当不希望类被继承时,可以用 final 修饰; // 如…