你的位置:首页 > 操作系统

[操作系统]Linux内核系列—7.操作系统开发之中断和异常


a.概述

中断门和陷阱门的作用机理几乎是一样的,只不过使用调用门时使用call指令,而这里我们使用int指令。中断门和陷阱门的结构如下图:

TYPE的4位将变为0xE(中断门)或0xF(陷阱门)。

指令int n产生中断时的情形如下图所示,n即为向量号,它类似于调用门的使用。

外部中断的情况复杂一些,因为需要建立硬件中断与向量号之间的对应关系。外部中断分为不可屏蔽中断(NMI)和可屏蔽中断两种,分别由CPU的两根引脚NMI和INTR来接收。如下图所示:

可屏蔽中断与CPU的关系是通过对可编程中断控制器8259A建立起来的。8259A可以认为它是中断机制中所有外围设备的一个代理。在BIOS初始化它的时候,IRQ0~IRQ7被设置为对应向量号08h~0Fh,在保护模式下向量号08h~0Fh已经被占用了,所以我们不得不重新设置主从8259A。

对8259A的设置并不复杂,通过向相应的端口写入特定的ICW来实现。主8259A对应的端口地址是20h和21h,从8259A对应的端口地址是A0h和A1h。ICW共有4个。初始化过程如下:

1.往端口20h(主片)或A0h(从片)写入ICW1.

2.往端口21h(主片)或A1h(从片)写入ICW2.

3.往端口21h(主片)或A1h(从片)写入ICW3.

4.往端口21h(主片)或A1h(从片)写入ICW4.

这4步的顺序是不能颠倒的。

ICW格式如下

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

我们通过对端口21h和A1h的操作屏蔽了所有的外部中断,写入OCW,在以下两种情况下用到它:

1.屏蔽或打开外部中断。

2.发送EOI给8259A以通知它中断处理结束。

若想屏蔽或打开外部中断,只需要往8259A写入OCW1就可以了。OCW1格式如下:

若想屏蔽某一个中断,将对应那一位设成1就可以了。EOI就是当每一次中断处理结束,需要发送一个EOI给8259A,以便继续接收中断。而发送EOI是通过往端口20h或A0h写OCW2来实现的。OCW2的格式如上图所示。

发送EOI给8259A可以由如下代码完成:

mov al, 20hout 20h或A0h, al

b.建立IDT

; IDT[SECTION .idt]ALIGN	32[BITS	32]LABEL_IDT:; 门            目标选择子,      偏移, DCount, 属性%rep 255		Gate	SelectorCode32, SpuriousHandler, 0, DA_386IGate%endrepIdtLen		equ	$ - LABEL_IDTIdtPtr		dw	IdtLen - 1	; 段界限		dd	0		; 基地址; END of [SECTION .idt]

每个描述符都设置为指向SelectorCode32:SpuriousHandler的中断门。

; 为加载 IDTR 作准备	xor	eax, eax	mov	ax, ds	shl	eax, 4	add	eax, LABEL_IDT		; eax <- idt 基地址	mov	dword [IdtPtr + 2], eax	; [IdtPtr + 2] <- idt 基地址; 关中断	cli	; 加载 IDTR	lidt	[IdtPtr]

c.实现一个中断

	call	Init8259A	int	080h

运行结果如下:

接下来修改IDT,把第80h号中断单独列出来。

; IDT[SECTION .idt]ALIGN	32[BITS	32]LABEL_IDT:; 门            目标选择子,      偏移, DCount, 属性%rep 128		Gate	SelectorCode32, SpuriousHandler, 0, DA_386IGate%endrep.080h:		Gate	SelectorCode32, UserIntHandler, 0, DA_386IGateIdtLen		equ	$ - LABEL_IDTIdtPtr		dw	IdtLen - 1	; 段界限		dd	0		; 基地址; END of [SECTION .idt]

运行结果如下:

 

 

一个码农的日常 

源码