【自制操作系统08】中断

由于中断这块的知识和代码都占较大篇幅,因此分成两章来讲,本章不包含任何中断的代码,只讲理论部分,以及中断的大概流程。代码实现部分由下一章来讲解

【自制操作系统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 私信我您的联系方式。

课程规划

本课程打算出系列课程,我写到哪觉得可以写成一篇文章了就写出来分享给大家,最终会完成一个功能全面的操作系统,我觉得这是最好的学习操作系统的方式了。所以中间遇到的各种坎也会写进去,如果你能持续跟进,跟着我一块写,必然会有很好的收货。即使没有,交个朋友也是好的哈哈。

目前的系列包括

posted @ 2020-02-16 18:03  闪客sun  阅读(2196)  评论(0编辑  收藏  举报