保护模式下的中断和异常(上) -- 硬件原理篇

2020-06-25 23:26:06   最后更新: 2020-06-25 23:26:06   访问数量:58




在我们这个系列的第一篇文章中,我们就已经使用过了 BIOS 10H 中断,用来在屏幕上打印一行字符:

计算机是如何启动的?如何制作自己的操作系统

 

除了 10H 中断,我们还使用过 BIOS 21H 中断,用来让实地址模式的程序退出,以及使用 15H 中断获取物理内存的信息:

保护模式进阶 -- 再回实模式

实战分页机制实现 -- 通过实际内存大小动态调整页表个数

 

在计算机体系中,硬件中断是触发 CPU 与其他硬件设备进行通信的重要方式

但你有没有发现,这些中断都是在实地址模式下使用的,一旦进入保护模式,我们就再没有使用过中断功能,那么,在保护模式中,我们是否还可以像保护模式中一样,通过 int 指令触发 BIOS 中断呢?答案是不可以,因为我们在进入保护模式前,通过 cli 指令关闭了硬件中断

但是,在系统运行中,常常会出现需要暂停当前流程,响应突发事件的中断场景,那么,有什么办法让我们能够在软件的层面实现硬件中断的类似效果呢?答案当然是可以的,本文就来详细介绍

 

异常

有时,我们运行了错误的指令,或指令执行时发生了错误,例如去计算除 0 的情况,或者前面提到的程序调用过程中错误特权级的切换等,这类异常情况就是系统中的“异常”

 

异常的分类

处理器预设了一系列异常,他们分为三类:

  1. Fault -- 可更正异常,也称为“故障”,这类异常一旦被更正,系统可以继续原来的程序执行下去,因此,在 fault 异常发生时,处理器会首先保存当前运行状态,在异常处理完成后自动加载刚才的状态重新执行并继续
  2. Trap -- 也称为“陷阱”,Fault 在发生时,EIP 值指向的是触发异常的指令,也就是说,在异常处理完成后,会重新执行触发异常的指令,而 Trap 发生时,EIP 指向的是触发异常的下一条指令,当异常处理完成后,CPU 会从下一条指令开始继续执行,Trap 的典型场景是调试中断,调试完成继续执行的时候,当然要调度下一条指令,而不是重新回到刚才的断点再调试一次
  3. Abort -- “中止”,是严重的异常,比如硬件错误和系统表中包含非法值或不一致的状态等,一旦这类异常发生,程序运行便随之中止

 

异常列表

CPU 预设的异常列表
向量号助记符类型描述来源
0#DE错误除零错误DVI和IDIV指令
1#DB错误/陷阱调试异常,用于软件调试任何代码或数据引用
2 中断NMI中断不可屏蔽的外部中断
3#BP陷阱断点INT 3指令
4#OF陷阱溢出INTO指令
5#BR错误数组越界BOUND指令
6#UD错误无效指令(没有定义的指令)UD2指令(奔腾Pro CPU引入此指令)或任何保留的指令
7#NM错误数学协处理器不存在或不可用浮点或WAIT/FWAIT指令
8#DF终止双重错误(Double Fault)任何可能产生异常的指令、不可屏蔽中断或可屏蔽中断
9#MF错误向协处理器传送操作数时检测到页错误(Page Fault)或段不存在,486及以后集成了协处理器,本错误就保留不用了浮点指令
10#TS错误无效TSS任务切换或访问TSS
11#NP错误段不存在加载段寄存器或访问系统段
12#SS错误栈段错误栈操作或加载SS寄存器
13#GP错误通用/一般保护异常,如果一个操作违反了保护模式下的规定,而且该情况不属于其他异常,CPU就是认为是该异常任何内存引用或保护性检查
14#PF错误页错误任何内存引用
15 保留  
16#MF错误浮点错误浮点或WAIT/FWAIT指令
17#AC错误对齐检查对内存中数据的引用(486CPU引入)
18#MC终止机器检查(Machine Check)错误代码和来源与型号有关(奔腾CPU引入)
19#XF错误SIMD浮点异常SIMD浮点指令(奔腾III CPU引入)
20~31 保留  
32~255用户自定义中断中断可屏蔽中断来自INTR的外部中断或INT n指令

 

中断

正常的程序运行,除了发生异常外,即便是发生跳转,也都是程序主动的行为,但有时,处理器外部的硬件事件,比如外围设备的请求突然到来等都是随机发生的,我们可以预先设定事件发生时执行的程序,但不能预知事件何时到来,这样的场景就是“中断”,也就是上表中标记为“Interrupt”的类型,另一个触发中断的方式是通过 int 指令手动触发,这就是中断产生的两大原因:

  1. 外部硬件中断,他又可以分为以下两种:
    1. NMI -- 不可屏蔽中断,向量号为 2
    2. INTR -- 可屏蔽中断

  2. int n 指令触发中断

 

不可屏蔽中断和可屏蔽中断分别是通过 CPU 的 NMI 引脚和 INTR 引脚触发的,顾名思义,可屏蔽中断对硬件中断实现了是否屏蔽的标识,这意味着更加灵活的中断控制,因此也是所有中断最为常用的类型

为了控制中断的屏蔽,以及在众多中断中控制中断触发的优先级等功能,CPU 在 INTR 引脚上级联了两个 8259A 芯片,8259A 芯片就是“可编程中断控制器”

如下图所示,这两个级联的 8259A 芯片,每一个都有 8 根中断信号线,从而可以挂接 15 个不同的外部设备,在实地址模式下,IRQ0 ~ IRQ7 被设置为了中断向量号 08h ~ 0Fh 的中断

 

 

8259A 的初始化

8259A 芯片有两种工作状态:

  1. 编程状态
  2. 操作状态

 

加电之初,8259A 处于编程状态,此时 CPU 可以通过 out 指令,向分别挂载在 20h、21h 端口和 A0h、A1h 端口的主 8259A 芯片和从 8259A 芯片写入特定的初始化指令来实现芯片的设置,这个特定的初始化指令被称为 ICW(Initialization Command Word),ICW 命令共有 4 个,分别是 ICW1、ICW2、ICW3、ICW4,他们必须从 1 到 4 依次进行初始化

ICW 具体取值如下:

 

 

可以看到,由于 80x86 体系约定使用主片的 IR2 引脚级联从片,我们就可以确定全部的 ICW 字段取值了,其中最为重要的是 ICW2 的中断向量号标识,他表示 IQR0 对应的中断向量号,此后,IQR1 ~ IQR7 会分别对应 IQR0 的中断向量号 + 1 ~ IQR0 的向量中断号 + 7

 

  • 下面的代码展示了如何初始化 8259A,在实地址模式或是保护模式下执行都可以,但只能执行一次,且必须按照顺序执行:
Init8259A: mov al, 011h ; ICW1,级联 8259A,需要 ICW4,8 字节中断向量,边沿触发 out 020h, al ; 主 8259A,ICW1 out 0A0h, al ; 从 8259A,ICW1 mov al, 020h ; 主 ICW2,IRQ0 中断向量设置为 0x20 out 021h, al ; 主 8259A,ICW2 mov al, 028h ; 从 ICW2,IRQ8 中断向量设置为 0x28 out 021h, al ; 从 8259A,ICW2 mov al, 004h ; 主 ICW3,IR2 引脚级联从 8259A out 021h, al ; 主 8259A,ICW3 mov al, 002h ; 从 ICW3,主 8259A 的 IR2 引脚级联从 8259A out 0A1h, al ; 从 8259A,ICW3 mov al, 001h ; ICW4,非自动 IOE,80x86 模式,无缓冲,非特殊完全嵌套方式 out 021h, al ; 主 8259A,ICW4 out 0A1h, al ; 从 8259A,ICW4

 

 

8259A 的操作控制 -- OCW

完成了上述初始化操作,8259A 就从编程状态进入了操作状态,此时我们可以通过操作控制字 OCW(Operation Control Word)来实现操作控制,虽然和 ICW 一样,OCW 也不只有一个,而是有 OCW1、OCW2、OCW3 三个,但实际上我们只需要使用 OCW1 和 OCW2

他们分别具有下面两个功能:

  1. OCW1 -- 屏蔽或打开外部中断,通过 021h 或 0A1h 端口发送
  2. OCW2 -- 发送 EOI 通知 8259A 中断处理完成,通过 020h 或 0A0h 端口发送

 

当我们需要屏蔽或打开外部中断时,只需要设置好 OCW1,然后通过 out 021h, OCW1 或者 out 0A1h, OCW1 就可以实现主 8259A 或是从 8259A 某个或某几个中断的屏蔽或打开

这个操作也同样被 8259A 芯片认为是一种中断,而此前我们通过 ICW4 设置了非自动 EOI 模式,所以需要在上述操作完成后通过 OCW2 发送 EOI 信号,OCW2 的 EOI 信号可以选择通过 020h 端口或 0A0h 端口发送给主或从 8259A 中的一个

下图展示了 OCW1 和 OCW2 的字段含义:

 

 

  • 下面的代码实现了仅开启定时器中断的功能:
mov al, 0FEh ; 主 OCW1,仅开启定时器中断 out 021h, al ; 主 8259A,OCW1 mov al, 0FFh ; 从 OCW1,关闭所有中断 out 0A1h, al ; 从 8259A,OCW1 mov al, 20h ; OCW2,发送 EOI 信号 out 020h, al ; 主 8259A,OCW2

 

 

本文我们详细介绍了保护模式下的中断和异常与实地址模式下的不同之处,以及如何通过程序操作硬件 -- 可编程中断控制器初始化、屏蔽或打开中断的响应,这些是理解硬件系统、操作系统的基础知识,也是硬件的部分

本文设计的实际开发内容比较少,你是否已经迫不及待的想要立即尝试一下如何通过程序让我们能够在保护模式下触发和响应中断呢?敬请期待下一篇文章中的实战吧

 

欢迎关注微信公众号,以技术为主,涉及历史、人文等多领域的学习与感悟,每周三到七篇推文,只有全部原创,只有干货没有鸡汤

 

 

《Orange's 一个操作系统的实现》

《linux 内核完全注释》

 






操作系统      os      异常      保护模式      中断      oranges      8259a     


京ICP备15018585号