天天看點

全面剖析《自己動手寫作業系統》第六章--中斷處理程式

轉載請标注:http://blog.csdn.net/zgh1988/article/details/7389329

下面我将分别以c和d為例,來講述單程序切換和多程序切換下的中斷處理程式

1、單程序環境下的中斷處理程式

2、多程序環境下的中斷處理程式

一、單程序環境下的中斷處理程式

    在這裡我們隻考慮利用時鐘中斷來進行程序切換。

    我們知道程序是運作在ring1環境下的,而中斷處理程式是運作在ring0環境下的,讓我們想象一下程序切換時的情形。一個程序正在兢兢業業地運作着,這時候時鐘中斷發生了,特權級從ring1跳到ring0,開始執行時鐘中斷處理程式,中斷處理程式這時調用程序排程子產品,指定下一個應用運作的程序,當中斷處理程式結束時,下一個程序準備就緒并開始運作,特權級又從ring0跳回到ring1。

    下面我們從兩個方面來考慮程序切換過程中需要注意什麼?(假設此時運作的是程序A)

1、現場的儲存和恢複

    進入中斷處理程式之後,首要任務就是儲存程序A的狀态資訊,以便于将來恢複程序A時使用。在本程式中,程序A的狀态資訊隻包含寄存器内容。由于是單程序環境下的中斷,是以在離開中斷處理程式之前,要恢複程序A的狀态資訊,離開中斷處理程式之後,就又傳回到程序A。

hwint00:
    ……
    ;儲存原寄存器的内容
    pushad
    push ds
    push es
    push fs
    push gs
    ………

    ;恢複原寄存器的内容
    pop gs
    pop fs
    pop es
    pop ds
    popad

    ……
    iretd
           

2、堆棧的切換

    中斷發生,特權級發生了變化,從ring1(程序A)----- ring0(中斷處理程式)-----ring1(程序A)。

    首先考慮ring1----ring0,從低特權級(ring1)跳轉到高特權級(ring0),需要從TSS中擷取es0和esp0。進入到ring0(中斷處理程式)之後,我們需要将程序A的狀态資訊儲存到PCB(程序控制塊)中,是以在發生中斷處理程式之前,我們将TSS中esp0指向程序表。是以在restart函數中,我們看到:

restart:
	mov	esp, [p_proc_ready]		;将程序表首位址指派給esp
	lldt	[esp + P_LDT_SEL]		;加載PCB中的LDT
	lea	eax, [esp + P_STACKTOP]		;将eax指向PCB中的eax的位址
	mov	dword [tss + TSS3_S_SP0], eax	;将TSS中的esp0指向PCB中的eax的位址
	
	;此時esp指向程序表首位址,連續出棧,依次彈出gs, fs, es, ds, edi
	;esi, ebp, esp, ebx, edx, ecx, eax。
	pop	gs
	pop	fs			
	pop	es
	pop	ds
	popad

 ;此時esp指向eip
	add	esp, 4
	iretd
           
全面剖析《自己動手寫作業系統》第六章--中斷處理程式

結合圖形來分析程式。

    我們知道,我們将esp0指向了資料結構PROCESS中STACK_FRAME的eax,是以在時鐘中斷發後,進入中斷處理程式,首先需要儲存程序狀态資訊,即通過一堆push指令來完成,剛好儲存到程序的PCB中。也就是PROCESS結構。

hwint00:		; Interrupt routine for irq 0 (the clock).
	sub esp, 4

	;儲存原寄存器的内容
	pushad
	push ds
	push es
	push fs
	push gs

	mov dx, ss
	mov ds, dx
	mov es, dx

	;改變螢幕第0行,第0列的字元
	;inc byte[gs:0]

	;使主8259接受中斷
	mov al, EOI
	out INT_M_CTL, al

	inc dword[k_reenter]
	cmp dword[k_reenter], 0
	jne .re_enter

	;切換到核心棧
	mov esp, StackTop

	;打開中斷
	sti

	;調用disp_str函數,用來顯示“^”
	push clock_int_msg
	call disp_str
	add esp, 4

	;調用delay函數,實作中斷嵌套
	;push 1
	;call delay
	;add esp, 4

	;關閉中斷
	cli

	;離開核心棧
	mov esp, [p_proc_ready]

	lea eax, [esp + P_STACKTOP]
	mov dword[tss + TSS3_S_SP0], eax

.re_enter:
	dec dword[k_reenter]

	;恢複原寄存器的内容
	pop gs
	pop fs
	pop es
	pop ds
	popad

	add esp, 4

	iretd
           

    在ring0中,我們需要調用程序排程子產品,或者顯示某些字元,需要使用堆棧,此時的esp指向的是程序表,是以如果對esp進行操作,相當于改變了程序表的内容。是以我們此時需要将esp儲存起來,然後将其指向另一塊堆棧(即核心棧)即:

mov esp, StackTop。

    執行完排程子產品,或者顯示某些字元之後,我們需要離開核心棧,那麼此時我們需要将esp指向何處呢?我們知道,在離開中斷處理程式之前,我們需要恢複程序A的狀态資訊,是以要通過一堆pop指令來完成。于是我們必須将esp指派為指向程序表的起始位址。即:mov esp, [p_proc_ready]

    這時候,我們還需要考慮一個問題,就是我們在離開中斷處理程式之前,應該還要完成一個任務,就是指派TSS中的esp0。是以我們使用

mov esp, [p_proc_ready]

lea eax, [esp + P_STACK_TOP]

mov dword[tss + TSS3_S_SP0], eax

這段代碼,與restart中指派esp0的一模一樣。

此時我們完成了ring0 --- ring1的跳轉。

二、多程序環境下的中斷處理程式

    在多程序切換環境下,要求多個程序進行切換,是以每次在發生中斷處理程式之後,我們需要從程序表中取出下一個等待的程序,然後去執行該程序。是以,在多程序環境下的中斷處理程式隻比之前多了一個任務,那就是在離開中斷處理程式之前,将一個等待的程序取出。     下面是程式代碼:

hwint00:		; Interrupt routine for irq 0 (the clock).
	sub esp, 4

	;儲存原寄存器的内容
	pushad
	push ds
	push es
	push fs
	push gs

	mov dx, ss
	mov ds, dx
	mov es, dx

	;使主8259接受中斷
	mov al, EOI
	out INT_M_CTL, al

	inc dword[k_reenter]
	cmp dword[k_reenter], 0
	jne .re_enter

	;切換到核心棧
	mov esp, StackTop

	;打開中斷
	sti
	
	;調用clock_handler函數,通過調整p_proc_ready的指向proc_table
	;數組中不同的程序。
	push 0
	call clock_handler
	add esp, 4

	;關閉中斷
	cli

	;離開核心棧
	mov esp, [p_proc_ready]
	lldt [esp + P_LDT_SEL]
	lea eax, [esp + P_STACKTOP]
	mov dword[tss + TSS3_S_SP0], eax

.re_enter:
	dec dword[k_reenter]

	;恢複原寄存器的内容
	pop gs
	pop fs
	pop es
	pop ds
	popad

	add esp, 4

	iretd
           

我們使用排程子產品,來完成了程序的排程,該程序排程簡單,就是依次運作程序表中的每一個程序。我們可以檢視clock_handler函數。它是這樣完成的:

PUBLIC void clock_handler(int irq)
{
	disp_str("#");
	p_proc_ready++;
	if (p_proc_ready >= proc_table + NR_TASKS)
	{
		p_proc_ready = proc_table;
	}
}
           

這樣之後,我們就完成了不同程序之間的切換。

全面剖析《自己動手寫作業系統》第六章--程序  http://blog.csdn.net/zgh1988/article/details/7371754

全面剖析《自己動手寫作業系統》第五章--makefile http://blog.csdn.net/zgh1988/article/details/7338380

全面剖析《自己動手寫作業系統》第五章---加載核心kernel.bin http://blog.csdn.net/zgh1988/article/details/7329941

全面剖析《自己動手寫作業系統》第五章---Red Hat 9.0 的安裝過程  http://blog.csdn.net/zgh1988/article/details/7315676

全面剖析《自己動手寫作業系統》第四章---FAT12檔案系統 http://blog.csdn.net/zgh1988/article/details/7284834

全面剖析《自己動手寫作業系統》第四章---加載Loader.bin http://blog.csdn.net/zgh1988/article/details/7291909

全面剖析《自己動手寫作業系統》第三章---進入保護模式   http://blog.csdn.net/zgh1988/article/details/7098981

全面剖析《自己動手寫作業系統》第三章---“實模式--保護模式--實模式” http://blog.csdn.net/zgh1988/article/details/7255804

全面剖析《自己動手寫作業系統》第三章---堆棧段的工作方式 http://blog.csdn.net/zgh1988/article/details/7256254

全面剖析《自己動手寫作業系統》第三章---特權級以及不同特權級代碼段之間的跳轉 http://blog.csdn.net/zgh1988/article/details/7262901

全面剖析《自己動手寫作業系統》第三章---分頁機制 http://blog.csdn.net/zgh1988/article/details/7270748

全面剖析《自己動手寫作業系統》第三章---中斷機制 http://blog.csdn.net/zgh1988/article/details/7276259

全面剖析《自己動手寫作業系統》第二章http://blog.csdn.net/zgh1988/article/details/7062065

全面剖析《自己動手寫作業系統》第一章http://blog.csdn.net/zgh1988/article/details/7060032

《自己動手寫作業系統》讀後感http://blog.csdn.net/zgh1988/article/details/7059936

繼續閱讀