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

news2024/11/16 0:34:30

前情提要

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

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.coloradmin.cn/o/1526918.html

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

相关文章

07|链(下):想学“育花”还是“插花”用RouterChain确定客户意图

任务设定 鲜花养护(保持花的健康、如何浇水、施肥等)鲜花装饰(如何搭配花、如何装饰场地等) 如果接到的是第一类问题,你要给ChatBot A指示;如果接到第二类的问题,你要给ChatBot B指示。 整体…

Window部署AgileConfig

AgileConfig:分布式配置中心 github:GitHub - dotnetcore/AgileConfig: 基于.NET Core开发的轻量级分布式配置中心 / .NET Core lightweight configuration server 下载部署包:Releases dotnetcore/AgileConfig GitHub 版本:…

MateBook D 14 SE版(2022版)使用体验

试用了一下华为的笔记本电脑,我觉得是出乎意料的好用,配置不算高,但是特别流程,而且自带了正版的office等软件,生产力杠杠的,外观也很不错,设计语言很高级,价格不贵,但是…

QGIS中天地图插件的安装与使用

概述 在QGIS中可添加xyz类型的切片为数据源,一般作为底图加载到地图上。在QGIS中添加xyz类型的切片的操作可参考QGIS CookBook。天地图提供的服务也是xyz类型的切片,但是为提高其加载速度,一般采用了t0-t7多个节点,在QGIS中添加x…

rocky9 编写一键安装mysql 的sh脚本

基本操作步骤 1、虚拟机最小化安装rocky9系统,安装后克隆一个系统;1个用来获取下载的rpm包,一个用来编写sh 测试脚本; 2、修改虚拟机的 yum配置文件,获取获取rpm程序 :启用缓存,并修改yum下载…

高效使用git流程分享

准备 假设你已经 clone 了当前仓库,并且你的终端位置已经位于仓库目录中。 查询状态 查询状态常用的命令有 git status 和 git branch。 前者用于查询更改文件情况,后者用于展示所有分支。 chatbot-system$ git status On branch develop Your bran…

RedisCluster集群中的插槽为什么是16384个?

RedisCluster集群中的插槽为什么是16384个? CRC16的算法原理。 1.根据CRC16的标准选择初值CRCIn的值2.将数据的第一个字节与CRCIn高8位异或3.判断最高位,若该位为0左移一位,若为1左移一位再与多项式Hex码异或4.重复3至9位全部移位计算结束5…

PHP姓名快速匿名化工具(重组脱敏)

PHP姓名重组工具(脱敏/匿名化工具) 将excel数据姓名列粘贴提交&#xff0c;得到随机姓随机中间字随机尾字的重组姓名 那些年自用瞎搞的代码&#xff0c;今日整理成网页交提交得到结果的交互功能分享。 <?php //PHP姓名重组工具(脱敏/匿名化工具) //将excel数据姓名列粘贴…

前端面试拼图-知识广度

摘要&#xff1a;最近&#xff0c;看了下慕课2周刷完n道面试题&#xff0c;记录并添加部分可参考的文档&#xff0c;如下... 1. 移动端H5 click有300ms延迟&#xff0c; 如何解决&#xff1f; 背景&#xff1a;double tap to zoom 移动端H5中的300ms点击延迟问题通常是由浏览…

Obsidian使用200+插件与70+种主题分享

主题资源 下载方式一&#xff1a; 网盘下载 密码:a3eu 下载方式二&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1fOgP8lY29sYxkUAbTlQQCw 提取码&#xff1a;qhxa 下载解压打开红色框文件夹 上面的是插件&#xff0c;下面的是主题 以下介绍安装主题 打开Obsidi…

定义一个符号常量,并计算

这段代码的输出结果是什么 #include <stdio.h> #define PI 32 int main() { int iPI*2; printf("i%d\n",i);} 是7。 我问了一下AI&#xff0c;AI也回答错了&#xff0c;这是个值得注意的地方。

华为鸿蒙生态杀疯了 大厂高薪急聘鸿蒙开发者

前言 最近有接收到个别小伙伴的疑惑&#xff0c;说鸿蒙现在前景不好就业又困难&#xff0c;为什么我偏偏选择了他呢&#xff1f; 但其实真正了解鸿蒙前景的小伙伴都知道&#xff0c;鸿蒙系统强势来袭&#xff0c;华为“纯血鸿蒙”技术开发人员急缺&#xff0c;薪资待遇极好&a…

云原生(四)、Docker-Compose

Docker-Compose Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。它使用一个简单的 YAML 文件来配置应用程序的服务、网络和卷&#xff0c;从而使得在不同环境中轻松部署应用程序变得更加简单和可靠。 Docker Compose 主要由以下几个核心组件组成&#xf…

DockerHub搜索并拉取一个Redis镜像

1&#xff09;去DockerHub搜索Redis镜像 2&#xff09;查看Redis镜像的名称和版本 3&#xff09;利用docker pull命令拉取镜像 4&#xff09;利用docker save命令将 redis:latest打包为一个redis.tar包 5&#xff09;利用docker rmi 删除本地的redis:latest 6&#xff09;利用…

小鹏MONA将至:10 - 15万级,用性价比打新势力,用智驾打比亚迪

‍ 作者 |老缅 编辑 |德新 小鹏的全新品牌即将发布&#xff0c;10-15万级也能有高等级智能驾驶。 3月16日在中国电动汽车百人会论坛2024上&#xff0c;小鹏汽车董事长、CEO何小鹏提出&#xff1a;“下一个十年将是智能化的十年。未来18个月内高阶智驾的拐点将到来”。 所谓…

Git——GitHub远端协作详解

目录 Git&GitHub1、将内容Push到GitHub上1.1、在GitHub上创建新项目1.2、upstream1.3、如果不想要相同的分支名称 2、Pull下载更新2.1、Fetch指令2.2、Fetch原理2.3、Pull指令2.4、PullRebase 3、为什么有时候推不上去3.1、问题复现3.2、解决方案一&#xff1a;先拉再推3.3…

深度学习神经网络相关记录《二》

如何判断模型是一个好模型&#xff1f; 模型预测效果&#xff0c;也就是模型预测的准确率运算速度&#xff1b;能够处理大量数据、短时间内急速学习、可以实时进行预测&#xff0c;是机器学习的重要优势&#xff1b;可解释性&#xff1b;深度学习已经不太关系这一点了&#xf…

面试算法-51-翻转二叉树

题目 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1] 解 class Solution {public TreeNode invertTree(TreeNode root) {dfs(root);re…

苍穹外卖-day09:用户端历史订单模块(理解业务逻辑),商家端订单管理模块(理解业务逻辑),校验收货地址是否超出配送范围(相关API)

用户端历史订单模块 1. 查询历史订单&#xff08;分页查询&#xff09; 1.1 需求分析和设计 产品原型&#xff1a; 业务规则 分页查询历史订单可以根据订单状态查询展示订单数据时&#xff0c;需要展示的数据包括&#xff1a;下单时间、订单状态、订单金额、订单明细&#…

“SRP模型+”多技术融合在生态环境脆弱性评价模型构建、时空格局演变分析与RSEI 指数的生态质量评价及拓展应用教程

原文链接&#xff1a;“SRP模型”多技术融合在生态环境脆弱性评价模型构建、时空格局演变分析与RSEI 指数的生态质量评价及拓展应用教程https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247597452&idx5&snf723d9e5858a269d00e15dbe2c7d3dc0&chksmfa823c6…