【自制操作系统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  阅读(2378)  评论(0)    收藏  举报