由于中断这块的知识和代码都占较大篇幅, 因此分成两章来讲, 本章不包含任何中断的代码, 只讲理论部分, 以及中断的大概流程. 代码实现部分由下一章来讲解
[自制操作系统 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] 深入浅出特权级
来源: https://www.cnblogs.com/flashsun/p/12310957.html