一个操作系统的实现(5):中断门

中断在linux驱动中占有很重要的地位,所以也一定要好好分析一下。还是一样的套路,先讲一下基本的概念,然后分析源码再来加深理解。

中断门与其他三个门(调用门、陷阱门、任务门)一起是操作系统里的四扇门。它们之间的区别以后再补充,今天重点分析中断门。

那么什么是门呢?门其实就是一中描述符,直观来看这个描述符描述了由一个选择子和一个偏移所指定的线性地址,程序正是通过这个地址进行转移的。中断描述符放在中断描述符表里(IDT),也就是这个IDT将中断向量和中断服务程序联系起来,他们之间的关系如下图:

一个操作系统的实现(5):中断门_第1张图片

那中断向量怎么来得呢,其实每一种中断(异常)都会对应一个中断向量号,具体有哪些中断向量,可以看下面这张图:

向量号 助记符 描述 类型 出错码
0 #DE 除法错 Fault DIV和IDIV指令
1 #DB 调试异常 Fault/Trap 任何代码和数据的访问
2 非屏蔽中断 Interrupt 非屏蔽外部中断
3 #BP 调试断点 Trap 指令INT 3
4 #OF 溢出 Trap 指令INTO
5 #BR 越界 Fault 指令BOUND
6 #UD 无效(未定义)操作码 Fault 指令UD2或无效指令
7 #NM 设备不可用(无数学协处理器) Fault 浮点或WAIT/FWAIT指令
8 #DF 双重错误 Abort 有(0) 所有能产生异常或NMI或INTR
的指令
9   协处理器段越界(保留) Fault 浮点指令(386后不再处理此
异常)
10 #TS 无效TSS Fault 任务切换或访问TSS时
11 #NP 段不存在 Fault 加载段寄存器或访问系统段时
12 #SS 堆栈段错误 Fault 堆栈操作或加载SS时
13 #GP 常规保护错误 Fault 内存或其他保护检验
14 #PF 页错误 Fault 内存访问
15 Intel保留,未使用      
16 #MF x87FPU浮点错(数学错) Fault x87FPU浮点指令或WAIT/FWAIT指令
17 #AC 对齐检验 Fault 有(0) 内存中的数据访问(486开始支持)
18 #MC Machine Check Abort 错误码(若有的话)和源依赖于
具体模式(奔腾CPU开始支持)
19 #XF SIMD浮点异常 Fault SSE和SSE2浮点指令(奔腾三
开始支持)
20~31 Inter保留,未使用      
32~255 用户定义中断 Interrupt   外部中断或int n指令

上图中除了两个Interrupt(中断)外,其他还有三种Fault、Trap、Abort异常。我们这里讨论的中断主要是用户定义中断,这种中断产生的原因有两种:一是外部中断,就是由硬件产生的中断;另一种是由指令int n产生的中断。

通过指令int n产生中断的情形如第一张图所示,这有点像调用门的适用。

外部中断的情况则复杂一些,因为需要建立硬件中断和向量号之间的对应关系。外部中断的简单示意图如下:

一个操作系统的实现(5):中断门_第2张图片

通过对8259A的配置,可将IRQ0~IRQ7对应到中断向量20h~27h,同样地IRQ8~IRQ15可对应到中断向量28h~2Fh。具体初始化配置代码就不进行分析了。


; IDT
[SECTION .idt]														;sect.idt #show#-->
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]	


[SECTION .s16]
[BITS	16]
LABEL_BEGIN:
	; 为加载 IDTR 作准备
	xor	eax, eax
	mov	ax, ds
	shl	eax, 4
	add	eax, LABEL_IDT		; eax <- idt 基地址
	mov	dword [IdtPtr + 2], eax	; [IdtPtr + 2] <- idt 基地址

	; 保存 IDTR
	sidt	[_SavedIDTR]

	; 保存中断屏蔽寄存器(IMREG)值
	in	al, 21h
	mov	[_SavedIMREG], al

	; 加载 GDTR
	lgdt	[GdtPtr]

	; 关中断
	;cli

	; 加载 IDTR
	lidt	[IdtPtr]
	; 打开地址线A20
	in	al, 92h
	or	al, 00000010b
	out	92h, al

	; 准备切换到保护模式
	mov	eax, cr0
	or	eax, 1
	mov	cr0, eax

	; 真正进入保护模式
	jmp	dword SelectorCode32:0	; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0  处

; 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, 11111111b	; 屏蔽主8259所有中断
	mov	al, 11111110b	; 仅仅开启定时器中断						
	out	021h, al	; 主8259, OCW1.
	call	io_delay

	mov	al, 11111111b	; 屏蔽从8259所有中断
	out	0A1h, al	; 从8259, OCW1.
	call	io_delay

	ret														
; Init8259A ---------------------------------------------------------------------------------------------


LABEL_SEG_CODE32:
	mov	ax, SelectorData
	mov	ds, ax			; 数据段选择子
	mov	es, ax
	mov	ax, SelectorVideo
	mov	gs, ax			; 视频段选择子

	mov	ax, SelectorStack
	mov	ss, ax			; 堆栈段选择子
	mov	esp, TopOfStack

	call	Init8259A

	int	080h													
	sti
	jmp	$													


; int handler ---------------------------------------------------------------													
_UserIntHandler:
UserIntHandler	equ	_UserIntHandler - $$
	mov	ah, 0Ch				; 0000: 黑底    1100: 红字
	mov	al, 'I'
	mov	[gs:((80 * 0 + 70) * 2)], ax	; 屏幕第 0 行, 第 70 列。
	iretd









你可能感兴趣的:(一个操作系统的实现(5):中断门)