作業系統--怎麼實現中斷

中斷髮生時,作業系統會為當前的任務建立一個快照,陷入內核,把CPU的控制權交給內核。內核趁這個機會做一些工作,比如調度執行其他任務。這只是中斷的作用之一。

使用中斷有一套固定的流程,掌握它即可。流程大概如下:

初始化8259A

初始化工作是對主從8259A的兩類埠賦值。這兩類埠是:ICW和OCW。

ICW

這是初始化命令字埠,一共有四個。

  1. ICW1,設置是否級聯、是否向ICW4寫入數據。
  2. ICW2,設置主從8259A的初始中斷向量號。注意,有一批中斷向量號已經在實模式下使用,設置初始中斷向量號應該避免覆蓋那批中斷向量號。
  3. ICW3,主片使用點陣圖標識幾號引腳掛載從片。從片使用低3位識別主片發送的數據的接收方是否是自己。
  4. ICW4,設置是否主動發送EOI。不清楚這點。

OCW

這是控制命令字埠。目前只需要操作一個。

設置屏蔽哪些埠、放行哪些埠。1表示屏蔽,0表示放行。

建立IDT

IDT是中斷向量表。類似GDT,也是記憶體中的一塊區域。但它內部包含的全是"門描述符"。

IDT中的每個門描述符的名稱是中斷向量號,選擇子是中斷向量號對應的處理中斷的程式碼。

; Gate : offset,selector,attr,paramCount
%macro Gate 4
dw	%1 & 0FFFFh
dw	%2 & 0FFFFh
dw	((%3 & 0FFh) << 8) | (%4 & 01Fh)
db	%1 >> 16
%endmacro

[SECTION .idt]
LABEL_IDT:
%rep 128
	Gate	selector32,	SpuriousHandler, attr, paramCount
%endrep
.080h:	Gate	selector32,	UserIntHandler, attr, paramCount

IDTLen	equ		$ - LABEL_IDT
IDTPtr	dw	IDTLen - 1
				dd	0

IDT中,第一個元素是可以用的,這與GDT不同。

%rep 128
	Gate	selector32,	offset, attr, paramCount
%endrep

表示IDT中有128個門描述符都指向相同的目標程式碼段。從0算起,128個門描述符,緊接著的中斷向量號應該是128,正好是80h。

建立中斷程式碼段

_SpuriousHandler:
SpuriousHandler	equ	_SpuriousHandler - $$
	mov al, 'A'
	mov ah, 0Fh
	mov [gs:(80*20+20)*2], ax
	
_UserIntHandler:
UserIntHandler	equ	_UserIntHandler - $$
	mov al, 'A'
	mov ah, 0Fh
	mov [gs:(80*20+20)*2], ax

上面的程式碼和IDT在同一程式碼段code32。SpuriousHandler、UserIntHandler是code32內的兩個偏移量。

不能完全理解這種寫法。不過,可暫且當作一種語法規則去記住就行了。以後有類似功能需要實現,就用這種寫法。

當中斷向量號是在0~127(包括0和127)時,會執行中斷處理程式碼SpuriousHandler。

當中斷向量號是080h時,會執行中斷處理程式碼UserIntHandler。

調用中斷

調用中斷的語句很簡單,如下:

int 00h
int 01h
int 10h
int 80h

特例--時鐘中斷

增加一個中斷的流程:

  1. 在IDT中增加一個門描述符,提供目標程式碼(中斷處理程式)的選擇子、偏移量等。
  2. 編寫中斷處理程式碼。
  3. 調用中斷。

時鐘中斷很特殊,不需要直接調用就能被調用。

編寫時鐘中斷的完整程式碼如下:

; Gate : offset,selector,attr,paramCount
%macro Gate 4
dw	%1 & 0FFFFh
dw	%2 & 0FFFFh
dw	((%3 & 0FFh) << 8) | (%4 & 01Fh)
db	%1 >> 16
%endmacro

[SECTION .idt]
LABEL_IDT:
%rep 128
	Gate	selector32,	SpuriousHandler, attr, paramCount
%endrep
.080h:	Gate	selector32,	UserIntHandler, attr, paramCount
.81h:		Gate	selector32,	ClockIntHandler, attr, paramCount	

IDTLen	equ		$ - LABEL_IDT
IDTPtr	dw	IDTLen - 1
				dd	0
				
_SpuriousHandler:
SpuriousHandler	equ	_SpuriousHandler - $$
	mov al, 'A'
	mov ah, 0Fh
	mov [gs:(80*20+20)*2], ax
	
_UserIntHandler:
UserIntHandler	equ	_UserIntHandler - $$
	mov al, 'A'
	mov ah, 0Fh
	mov [gs:(80*20+20)*2], ax
  
_ClockIntHandler:
ClockIntHandler	equ	_ClockIntHandler - $$
	inc [gs:(80*20+20)*2]
	
int 80h
;在死循環中,時鐘中斷會以一定的時間間隔發生。
jmp $

[gs:(80*20+20)*2]這塊記憶體已經有數據了,inc [gs:(80*20+20)*2]會增加這塊記憶體中的數據的值,效果是在螢幕上出現不斷變換的字母。

Tags: