【自制操作系统08】中断
闪客sun 人气:3由于中断这块的知识和代码都占较大篇幅,因此分成两章来讲,本章不包含任何中断的代码,只讲理论部分,以及中断的大概流程。代码实现部分由下一章来讲解
【自制操作系统09】中断的代码实现
一、到目前为止的程序流程图
为了让大家清楚目前的程序进度,画了到目前为止的程序流程图,如下。
二、什么是中断
这里我们先从形象的角度来描述,中断就是让操作系统停止手中正在进行的工作,先把中断信号对应的处理程序执行完毕,再回到之前的程序中继续进行,这样一个机制。
一个很形象的说法是,我们的操作系统就是 中断驱动 的,可以把操作系统简单理解为一个 死循环,无时无刻不在等待中断的来临,被动 地执行相应的任务。
while(true){
操作系统代码
}
三、中断的分类
外部中断
外部中断通过两个引脚连接到 CPU 上,一个是可屏蔽中断 INTR,一个是不可屏蔽中断 NMI
- INTR:硬盘、打印机、网卡等设备发出的中断信号,可通过 eflags 寄存器的 IF 位将所有这些外部设备的中断屏蔽
- NMI:电源掉电、内存读写错误、总线奇偶校验错误等灾难性的错误,不可屏蔽,CPU 必须立刻处理
对于可屏蔽中断,Linux 的处理方式是分成 上半部 和 下半部。上半部执行时关闭中断,立刻执行完毕;下半部执行时打开中断,此时如果有其他中断进来,则让给其他中断(也是上半部执行完毕)。
内部中断
内部中断可分为 软中断 和 异常,二者均是不可屏蔽的(即不受 eflags 的 IF 位影响)
- 软中断:就是软件发起的中断,最常见的也是我们之后进行系统调用的,就是 int 8位立即数,可表示 256 中中断。还有一些不常用的,甚至可以叫做异常,下面简单列出
- int3:中断向量号3,调试断点指令
- into:中断向量号4,中断溢出指令
- bound:中断向量号5,检查数组索引越界指令
- ud2:中断向量号6,未定义指令,常用于软件测试中主动发起这个中断
- 异常:指令执行期间 CPU 内部产生的错误引起,如分母为 0 将发起 6 号中断(异常),未定义的指令发起 6 号中断
- Fault(故障):可恢复的错误。发生此中断时,CPU 将机器状态恢复到异常之前的状态,之后调用中断处理程序,结束后返回。常见的如 缺页异常
- Trap(陷阱):有意的异常。通常是调试程序中用 int3 指令主动触发。
- Abort(终止):不可恢复的异常。直接将此程序从进程表中去掉。
四、中断号
我们知道一个中断对应着一个 中断号(中断向量号),下面列表说明
中断号 | 含义 | 来源 | 类型 | 是否有错误码 |
---|---|---|---|---|
0 | divide error | DIV and IDIV instructions | Fault | 无 |
1 | debug | any code or data reference | Fault/Trap | 无 |
2 | NMI Interrupt | NMI | Interrupt | 无 |
3 | Breakpoint | INT3 instruction | Trap | 无 |
4 | Overflow | INTO instruction | Trap | 无 |
5 | bound range exceeded | BOUND instruction | Fault | 无 |
6 | invalid opcode | UD2 instruction or reserved opcode.1 | Fault | 无 |
7 | device not available | floationg-point or WAIT/FWAIT instruction | Fault | 无 |
8 | double fault | any instruction that can generate an exception, an NMI, or an INTR | Fault | Y(0) |
9 | CoProcessor Segment Overrun | Floating-point instruction.2 | Fault | 无 |
10 | invalid TSS | task switch or TSS access | Fault | Y |
11 | segment not present | loading segment registers or accessing system segments | Fault | Y |
12 | stack segment fault | stack operations and SS register loads | Fault | Y |
13 | general protection | any memory reference and other protection checks | Fault | Y |
14 | page fault | any memory reference | Fault | Y |
15 | reserved | |||
16 | floating-point error | floating-point or WAIT/FWAIT instruction | Fault | 无 |
17 | alignment check | any data refrence in memory.3 | Fault | Y(0) |
18 | machine check | error codes and source are model dependent.4 | Fault | 无 |
19 | SIMD floating-point exception | SIMD floating-point instruction5 | Fault | 无 |
20-31 | reserved | |||
32-255 | maskable interrupts | External Interrupt from INTR pin or INT n instruction | Interrupt | 无 |
五、中断描述符表 IDT
我们先来回顾一下上一讲 【自制操作系统07】深入浅出特权级 说的四种门描述符
门 | type值 | 存在位置 | 用法 |
---|---|---|---|
任务门 | 0101 | GDT、LDT、IDT | 与TSS配合实现任务切换,不过大多数操作系统都不这么玩 |
中断门 | 1110 | IDT | 进入中断后屏蔽中断(eflags的IF位置0),linux利用此实现系统调用,int 0x80 |
陷阱门 | 1111 | IDT | 进入中断后不屏蔽中断 |
调用门 | 1100 | GDT、LDT | 用户用call或jmp指令从用户进程进入0特权级 |
你看,正如上一讲所说,中断门进入后先是屏蔽了中断,也就是中断例程的 上半部,程序中可以随时打开中断,也就自然到了 下半部,这就是 linux 系统的处理方式。
如何找到中断描述符表呢?你猜的没错,正如找 段描述符表,页表 等一样,有个 IDTR 寄存器存储它的位置(0-15位是表界限,16-47位表示表基址),有个 lidt 指令负责加载 IDTR。经典做法,我们见过太多次了,就不多说啦,不理解的可以从本系列开头开始看哟。
六、中断处理过程
上图就表示了整个中断处理的过程,不过还有几处图中没有显示
特权级检查:CPL <= 门描述符DPL && CPL > 目标代码段DPL
栈的处理:将 CS、EIP、EFLAGS、SS、ESP 寄存器的值压入中断处理程序使用的栈
七、8259A芯片
我们之前说过,外部设备发出中断信号,进入 CPU 的 INT 引脚上。但如果有多个外部设备近乎同时发送中断信号,CPU 先处理哪一个呢?未被处理的中断信号又记录在哪里呢?这时候就需要有个 中间的代理设备 来负责这个事情。
这个代理设备叫做 可编程中断控制器 PIC,其中 8259A 芯片是最常见的一种,我们这里把它的内部结构展示出来,由于是硬件相关,就不展开细说了,但由于之后要为其进行编程,所以大家先有个印象。
八、中断代码实现
由于到此篇幅过长,且中断代码的实现也是需要很大篇幅描述的,包括 可编程中断控制器的初始化,IDT 的初始化,以及中断例程代码的编写,所以将放在下一章进行讲解。
【自制操作系统09】中断的代码实现
写在最后:开源项目和课程规划
如果你对自制一个操作系统感兴趣,不妨跟随这个系列课程看下去,甚至加入我们,一起来开发。
参考书籍
《操作系统真相还原》这本书真的赞!强烈推荐
项目开源
项目开源地址:https://gitee.com/sunym1993/flashos
当你看到该文章时,代码可能已经比文章中的又多写了一些部分了。你可以通过提交记录历史来查看历史的代码,我会慢慢梳理提交历史以及项目说明文档,争取给每一课都准备一个可执行的代码。当然文章中的代码也是全的,采用复制粘贴的方式也是完全可以的。
如果你有兴趣加入这个自制操作系统的大军,也可以在留言区留下您的联系方式,或者在 gitee 私信我您的联系方式。
课程规划
本课程打算出系列课程,我写到哪觉得可以写成一篇文章了就写出来分享给大家,最终会完成一个功能全面的操作系统,我觉得这是最好的学习操作系统的方式了。所以中间遇到的各种坎也会写进去,如果你能持续跟进,跟着我一块写,必然会有很好的收货。即使没有,交个朋友也是好的哈哈。
目前的系列包括
- 【自制操作系统01】硬核讲解计算机的启动过程
- 【自制操作系统02】环境准备与启动区实现
- 【自制操作系统03】读取硬盘中的数据
- 【自制操作系统04】从实模式到保护模式
- 【自制操作系统05】开启内存分页机制
- 【自制操作系统06】终于开始用 C 语言了,第一行内核代码!
- 【自制操作系统07】深入浅出特权级
加载全部内容