本节,我们将学习现代操作系统的一个重要的特点——中断处理机制。同样,中断处理也是软硬件协同完成的,其中硬件部分:8259A负责可编程中断控制器,通过读写端口,实现中断信号和中断号的绑定等工作;CPU通过门描述符与中断向量表来实现中断服务程序。最后,访问一个中断和访问一个门的过程是类似的,读者可以进行相应比较。本文,主要通过pmtest9讲解中断程序的编写。
中断门的实现包括如下内容:设置8259A,进行中断硬件信息的读写;建立IDT;实现一个中断。1.预览
保护模式下,中断机制发生了很大的变化。中断向量表被IDT所取代,实模式下能够使用的BIOS中断保护模式下已经不能使用了。IDT,中断描述符表,里面存放着中断、陷阱、任务门,将每一个中断向量和描述符表对应起来。中断门和陷阱门的描述符如下:
从中断向量到中断处理程序的对应过程:
调用门和中断的处理机制类似,不同的是,前者用call指令,后者用int 指令。另外,linux中没有用到任务门,所以我们不再此做过多讲解。2.中断和异常机制
两个基本问题:处理器可以相应何种类型的中断?收到中断如何进行处理?
保护模式下的中断和异常介绍可以参考这里:
保护模式下的异常和中断
2.1对于异常
注意FAULT、TRAP、ABORT的区别和联系。
2.2对于中断
外部中断需要建立中断和向量号之间的联系。外部中断分为不可屏蔽中断和可以屏蔽中断,分别由CPU的NMI和INTR接收。结构如下图:
可屏蔽中断和CPU的关系是通过设置可编程中断控制器实现的。那么如何将中断请求与中断向量对应起来呢?BIOS在初始化的时候,
IR0~IR7被设置为对应向量08h~0fh,而通过表,我们知道保护模式下的向量号08h~0fh已经被占用,我们只能从新设置8259A
,这可以通过向特定的端口写入ICW(Initialzation Command Word)来实现的。
3.编程操作8259A
3.1什么是端口
端口是指接口电路中的一些寄存器,这些寄存器分别用来存放数据信息、控制信息和状态信息,相应的端口分别称为数据端口、控制端口和状态端口。
电脑运行的系统程序,其实就像一个闭合的圆圈,但是电脑是为人服务的,他需要接受一些指令,并且要按照指令调整系统功能来工作,于是系统程序设计者,就把这个圆圈截成好多段,这些线段接口就叫端口(通俗讲是断口,就是中断),系统运行到这些端口时,一看端口是否打开或关闭,如果关闭,就是绳子接通了,系统往下运行,如果端口是打开的,系统就得到命令,有外部数据输入,接受外部数据并执行。
其中,主8259A和从8259A对应的端口地址是20h/21h和A0h/A1h。
3.2ICW的格式与意义
相关代码:
; Init8259A ---------------------------------------------------------------------------------------------
Init8259A:
mov al, 011h
out 020h, al ; 主8259, ICW1.
call io_delay
out 0A0h, al ; 从8259, ICW1.
call io_delay
mov al, 020h ; IRQ0 对应中断向量 0x20
out 021h, al ; 主8259, ICW2.
call io_delay
mov al, 028h ; IRQ8 对应中断向量 0x28
out 0A1h, al ; 从8259, ICW2.
call io_delay
mov al, 004h ; IR2 对应从8259
out 021h, al ; 主8259, ICW3.
call io_delay
mov al, 002h ; 对应主8259的 IR2
out 0A1h, al ; 从8259, ICW3.
call io_delay
mov al, 001h
out 021h, al ; 主8259, ICW4.
call io_delay
out 0A1h, al ; 从8259, ICW4.
call io_delay
mov al, 11111110b ; 仅仅开启定时器中断
;mov al, 11111111b ; 屏蔽主8259所有中断
out 021h, al ; 主8259, OCW1.
call io_delay
mov al, 11111111b ; 屏蔽从8259所有中断
out 0A1h, al ; 从8259, OCW1.
call io_delay
ret
; Init8259A ---------------------------------------------------------------------------------------------
上述代码包括两部分:中断向量的设置部分+中断屏蔽的设置部分。其中,屏蔽的设置部分是通过OCW来完成的。OCW:是operation Control Word,OCW1用于设置屏蔽信息,OCW2用于发送EOI信号(end of int),表示中断处理完成,可以接受下一个中断了。OCW的设置是通过21h和A1h实现的,结构意义如下:
4.建立IDT
这一步和设置GDT类;IDT初始化以后,无法从保护模式回到实模式,因为IDTR和8259A的内容已经被改变了。注意观察一下,IDTR前后的变化。
5.时钟中断实验
这里,我们将打开时钟中断IRQ0.修改初始化8259A的代码,使能IRQ0;设定IDT;绑定中断处理函数。开启时钟中断: mov al, 11111110b ; 仅仅开启定时器中断
设定IDT:
; IDT
[SECTION .idt]
ALIGN 32
[BITS 32]
LABEL_IDT:
; 门 目标选择子, 偏移, DCount, 属性
%rep 32
Gate SelectorCode32, SpuriousHandler, 0, DA_386IGate
%endrep
.020h: Gate SelectorCode32, ClockHandler, 0, DA_386IGate
%rep 95
Gate SelectorCode32, SpuriousHandler, 0, DA_386IGate
%endrep
.080h: Gate SelectorCode32, UserIntHandler, 0, DA_386IGate
IdtLen equ $ - LABEL_IDT
IdtPtr dw IdtLen - 1 ; 段界限
dd 0 ; 基地址
; END of [SECTION .idt]
绑定中断处理函数:
; int handler ---------------------------------------------------------------绑定:
_ClockHandler:
ClockHandler equ _ClockHandler - $$
inc byte [gs:((80 * 0 + 70) * 2)] ; 屏幕第 0 行, 第 70 列。
mov al, 20h
out 20h, al ; 发送 EOI
iretdmov al, 020h ; IRQ0 对应中断向量 0x20 out 021h, al ; 主8259, ICW2. call io_delay
开中断:int 080h xchg bx,bx sti jmp $
6.其他
中断门和调用门相比,需要入栈的还有Eflags(和error code),堆栈切换情况如下(可能有特权级的切换):从中断或者异常返回要用iretd,和ret相比,它会改变EFLAGS。另外iretd执行的时候,error code并不会自动从堆栈弹出,需要手动清除。中断门和陷阱门的区别在于陷阱门产生的中断不会改变IF.