2012-02-14 130 views
2

我想通過IDT處理內核中斷。 我在Linux下使用Intel x86。x86:中斷處理程序循環

我已經設置了我的IDT和我的中斷條目,並且我啓動了一些測試來查看我的中斷處理程序。

當我嘗試int $0x0時,它完美地工作:我的處理程序被調用,但是當我嘗試一些錯誤代碼推入的異常時,我輸入了一個無限循環。

架構如下:

當異常到達時,我的處理程序的第一部分是ASM和調用一個常見的C部分。

my_handler.c

void handler(int i) 
{ 
    printf("Exception %d caught\n", i); 
} 

my_handlers.S

common: 
    pushal 

    pushl %ds 
    pushl %es 
    pushl %fs 
    pushl %gs 

    addl $48, %esp     // 4 4-bytes segments pushed 
            // + 8 4-bytes registers (pushal) 
`         // esp points on exception code 

    call handler     // call the C handler with exception code 

    subl $48, %esp 

    popl %gs 
    popl %fs 
    popl %es 
    popl %ds 

    popal 

    addl $8, %esp     // 4-byte error code + 4-byte exception number 
    iret 


exception_de_handler: 
    pushl $0      // Fake error code 
    pushl $0      // interrupt number 
    jmp common 

exception_gp_handler: 
    // error code is pushed by µproc. 
    pushl $13      // interrupt number 
    jmp common 

exception_pf_handler: 
    // error code is pushed by µproc. 
    pushl $14      // interrupt number 
    jmp common 

如果我嘗試運行followig代碼:

int* a = 0x0; 
*a = 42; 

它的工作原理,在exceution的*a = 42;

後恢復

但如果我嘗試:

int* a = 0x0; 
*a = 42; 
*a = 1337; 

它進入無限循環:

Exception 14 caught 
Exception 13 caught 
Exception 13 caught 
Exception 13 caught 
Exception 13 caught 
     ..... 
Exception 13 caught 
Exception 13 caught 
Exception 13 caught 
     ..... 

爲什麼第一個例外頁面錯誤(14)對一般保護(13)來處理,然後循環?

謝謝你的回答。

+1

爲什麼在Linux下手動操作IDT?那不可能很好地結束。您應該使用Linux的設備驅動程序API來處理中斷。 (另外我不確定你如何從內核模式調用'printf' - 我們可能需要更多解釋你實際做了什麼。) – zwol 2012-02-14 23:06:03

+0

@Zack - 好點 – Stewart 2012-02-15 00:28:12

回答

2

我認爲你搞砸了你的堆棧。你需要非常小心你在中斷處理程序中對你的堆棧做了什麼。在這種情況下,似乎你做到以下幾點: -

推錯誤代碼(可以由CPU來完成) 推的REG 推段的REG

添加0x48到堆棧指針,風堆棧一路備份,所以它指向錯誤代碼。

調用你的C函數

這實際上做的是「解放」您的堆棧段寄存器分別存放在的部分是什麼。事實上,你甚至不需要擔心在C函數所有這些都是因爲返回地址在呼叫指令中被壓入堆棧,並在您進入C調用之前吹走了ds和es的記錄。當你從C調回來時,你試着整理這個callstack,但是你沒有把它弄清楚 - 部分原因是你已經搞砸了,部分原因是你沒有清理過堆棧函數調用(假定處理程序使用_cdecl調用約定)。

這會導致您彈出ds的僞造值。當你把它加載到ds時,CPU檢查GDT的值,發現它是無效的。在這一點上,它提出了一個你正在尋找的GPF(例外13)。這在一定程度上可以恢復堆棧(CPU正在爲你照顧SS)並且保留ds的舊值 - 所以你永遠不會改變ds,這允許你再次運行printf。

您需要更加小心地調整堆棧,並且每當添加到堆棧指針時,都需要考慮在該範圍內的數據永遠消失,因爲下一個人或者可能是意想不到的打斷,會打擾你。