外部硬件中断
外部硬件中断就是从处理器外面来的中断信号,优先级很高。
它的实现是通过两个信号线引入处理器内部的,分别叫NMI
和INTR
。
非屏蔽中断(Non Maskable Interrupt,NMI)
需要两条线的原因是不一定每一个中断的优先级都需要立刻处理。当涉及到非屏蔽中断,通常是一些紧急且必须立即处理的硬件事件。
硬件中断的优先级管理:某些系统可能会使用外部硬件中断来处理中断请求的优先级。这意味着当多个中断同时发生时,不是所有中断都需要立即处理。处理器可以为不同的中断信号分配不同的优先级,从而决定哪些中断需要立即处理,而哪些可以等待。
需要立即处理的:
- 电池电量低,系统会发出一个中断告诉处理器快没电了。
- 内存访问电路有校验错误(读取错误)。
不那么着急处理的:
举个例子,一个嵌入式系统可能有多个外部硬件设备连接到处理器,包括键盘、鼠标和网络适配器。如果用户同时按下键盘上的按键和鼠标点击,系统可以根据设定的中断优先级来决定哪个中断首先得到处理。通常情况下,键盘中断可能具有较高的优先级,因为用户通常期望键盘响应更快,而鼠标中断则有较低的优先级,因为它不太紧急。
中断信号的来教叫做 中断源。传统的兼容模式下,NMI的中断源通过一个与非门连接到处理器,处理器的NMI引脚是高电平有效,中断信号是低电平有效,也就是NMI引脚是低电平则没有中断。
即使NMI与非门的输出为高时,也不能立刻被识别,还需要至少维持4个以上的时钟周期才算有效。
处理器还应当知道发生了什么事,以便于采用适当的处理措施。每个类型的中断都被统一编号,被称为中断类型号、中断向量或者中断号。而有些比较严重的事件就在实模式下被赋予了统一的中断号 2 ,一旦发生 2 号中断,处理器和软件系统通常会放弃继续正常工作的念头,也不会试图纠正已经发生的问题和错误。
可屏蔽中断(Interrupt Request,INTR)1,7,4,9,2,5
由于可能有多台设备同时发送中断请求,但是处理器每次只能处理一个中断,这就需要一个中间层进行仲裁,优先处理优先级更高的。个人计算机中使用最多的就是8259芯片
。Intel处理器允许256个中断,8259负责15个不固定的中断号,8个中断输入引脚。引脚的IR0
优先级向IR7
递减,从片是级联在主片的IR2
引脚。
个人计算机中有两块8259芯片
,并且两块的关系是级联:主片的INT输出
直接送到处理器INTR
引脚;从片的INT输出
送到第一块的引脚2上,一共提供15个中断信号。
- 主片的引脚 0 接的是系统定时器/定时器芯片,主片的端口号是
0x20
和0x21
- 从片的引脚 0 接的是实时时钟芯片(RTC),从片的端口号是
0xa0
和0xa1
8259内部有中断屏蔽寄存器(IMR)8位寄SPSCQueue *SPSCQueueInit(int capacity);
void SPSCQueuePush(SPSCQueue *queue, void *s);
void *SPSCQueuePop(SPSCQueue *queue);
void SPSCQueueDestory(SPSCQueue *);
即使中断从8529发送出去,最终解释权也归处理器。处理器内部的标志寄存器有个IF
标志位(中断标志)。当IF
为0,中断信号被忽略。
对于IF标志位的处理有两条指令:cli
(Clear Interrupt Flag)和sti
(SeT Interrupt flag)。
中断嵌套:当一个中断事件正在处理时,如果来了优先级更高的中断事件,允许暂时中止当前的中断事件,先为优先级较高的中断事件服务。
实模式下的中断向量表
中断处理实际上就是处理器执行一段与该中断有关的指令。因为可以识别256个中断,理论上就需要256段程序,在实模式下,处理器要求将它们的入口点集中存放在内存中从地址0x00000
到0x003ff
,共1kb
的空间内,这就是中断向量表(Interrupt Vector Table,IVT)。
每个中断在中断向量表中占2个字,分别是中断处理程序的偏移地址和段地址。
中断发生时,处理器执行完当前指令后会立刻为硬件服务,会先响应中断,告诉处理器处理该中断,然后向8259芯片所要中断号。
处理器拿到中断号会干下面的事情:
- 保护断点的现场。将标志寄存器FLAGS压栈,清除其
IF
位和TF
位(陷阱标志)。然后将当前的CS
和IP
压栈。(清除IF
是因为在此期间不再响应硬件中断,如果希望更高优先级的中断嵌套,可以编写程序时,使用sti
开放中断) - 执行中断程序。拿到中断号,将其乘4,得到其在中断向量表的偏移地址。从表中取出来对应段地址和偏移地址传入
CS
和IP
。 - 返回到断点继续执行。所有中断处理程序的最后一条必须是中断返回指令
iret
。依次恢复IP
,CS
,FLAGS
的原始数值。
而NMI
不会从外部获得中断号,自动生成中断号码 2,其他过程相同。
实时时钟、CMOS RAM 和 BCD编码
计算机为什么能精准显示日期和时间?在外围设备控制器芯片ICH
内部,集成了实时时钟电路(RTC)和两小块由互补金属氧化物材料组成的静态存储器(CMOS RAM)。实时时钟电路负责计时,日期和时间存储在静态存储器中。存储方式通常是二进制编码的十进制数(BCD)。
实时时钟是全天候跳动的,即使断电。因为主板上有个小电池供电。
日期和时间信息存储在CMOS RAM
中,通常有128字节,日期和时间信息只占了一小部分容量,其他空间用于保存整机的配置信息(硬件类型、工作参数、开机密码、辅助设备启动顺序)
CMOS RAM
的访问需要通过两个端口:0x70
或者0x74
是索引端口,用来指定CMOS RAM
内的单元;0x71
或者0x75
是数据端口,用来读写对应单元内的内容。
偏移地址 | 内容 | 偏移地址 | 内容 |
---|---|---|---|
0x00 | 秒 | 0x01 | 闹钟秒 |
0x02 | 分 | 0x03 | 闹钟分 |
0x04 | 时 | 0x05 | 闹钟时 |
0x06 | 星期 | 0x07 | 日 |
0x08 | 月 | 0x09 | 年 |
0x0A | 寄存器A | 0x0B | 寄存器B |
0x0C | 寄存器C | 0x0D | 寄存器D |
下面的例子是用来获取当时是星期几:
1 | mov al,0x06 |
out 0x70, al
: 这是一个输出指令,用于将AL寄存器中的值(在这里是0x06)写入到I/O端口0x70。在x86架构中,0x70端口通常用于控制实时时钟芯片的寄存器。这个操作可能用于设置实时时钟的某些配置,例如将其配置为读取或写入日期和时间信息。in al, 0x71
: 这是一个输入指令,用于从I/O端口0x71读取数据,并将读取的数据存储到AL寄存器中。在这个上下文中,0x71端口通常用于读取实时时钟的状态或数据,例如当前的时间信息。
端口的最高位是控制NMI
中断的开关。为0时,允许NMI
中断到达处理器;为1时,阻断所有NMI
信号,它不是中断信号,但是能控制与非门的输出。其他七个比特实际上用于指定CMOS RAM
单元的索引号。
初始化8259、RTC和中断向量表
寄存器A:寄存器A通常用于配置RTC的中断频率。通过设置寄存器A的值,你可以定义RTC中断的触发频率,例如每秒触发一次。这通常涉及到配置寄存器A的低4位(Rate Selector)。
寄存器B:寄存器B通常包含一些控制中断的位。它允许你启用或禁用RTC中断,并可能还有其他控制位,例如24小时模式、BCD码格式等。
寄存器C:寄存器C用于指示中断的发生。当RTC触发中断时,寄存器C的某些位会被设置,以告知系统发生了中断。
寄存器D:寄存器D通常包含一些额外的控制位,可能与其他RTC功能有关。