1. 程式人生 > >linux在x86上的中斷處理過程(詳細)

linux在x86上的中斷處理過程(詳細)

Linux在x86上的中斷處理過程

一:引言

在Intel的文件中,把中斷分為兩種。一種是異常,也叫同步同斷。一種稱之為中斷,也叫異常中斷。同步中斷指的是由CPU控制單元產生,之所以稱之為同步,是因為只有一條指令執行完畢後才會發出中斷。例如除法運算中,除數為零的時候,就會產生一個異常。非同步中斷是由外部裝置按照CPU的時鐘隨機產生的。例如,網絡卡檢測到一個數據到來就會產生一箇中斷。

二:x86的中斷處理過程

由於中斷是開著的,所以當執行完一條指令後,cs和eip這對暫存器中已經包含了下一條將要執行的指令的邏輯地址。在處理那條指令之前,控制單元會檢查在執行前一條指令時是否發生了一箇中斷或異常。如果發生了一箇中斷和異常,那麼控制單元執行下列操作:

1. 確定與中斷或異常關聯的向量i(0≤ i ≤255)
        2. 讀由idtr暫存器指向的IDT表中的第i項。
        3. 從gdtr暫存器獲得GDT的基地址,並在GDT中查詢,以讀取IDT表項中的選擇符標識的段描述符。這個描述符指定中斷或異常處理程式所在的段的基地址。
        4. 確信中斷是由授權的(中斷)發生源發出的。首先將當前特權級CPL(存放在cs暫存器的低兩位)與段描述符(存放在GDT中)的描述符特權級DPL比較。如果CPL小於DPL,就產生一個“通常保護”異常,因為中斷處理程式的特權級不能低於引起中斷的程式的特權。對於程式設計異常,則做進一步的安全檢查:比較CPL與處於IDT中的門描述符的DPL,如果DPL小於CPL,就產生一個“通常保護”異常,這最後一個檢查可以避免使用者應用程式訪問特殊的陷阱門和中斷門。
        5. 檢查是否發生了特權級的變化,也就是說,CPL是否不同於所選擇的段描述符的DPL。如果是,控制單元必須開始使用與新的特權級相關的棧,通過執行以下步驟來保證這一點:
                A. 讀tr暫存器,以訪問執行程序的TSS段。
                B. 用與新特權級相關的棧段和棧指標的正確值裝載ss和esp暫存器。這些值可以在TSS中找到。
                C. 在新的棧中儲存ss和esp以前的值,這些值定義了與舊特權級相關的棧的邏輯地址。
        6. 如果故障已發生,用引起異常的指令地址裝載cs和eip暫存器,從而使得這條指令能再次被執行。
        7. 在棧中儲存eflag、cs和eip的內容。
        8. 如果異常產生了一個硬體出錯碼,則將它儲存在棧中。
        9. 裝載cs和eip暫存器,其值分別是IDT表中第i項門描述符的段選擇符和偏移量欄位。這些值給出了中斷或者異常處理程式的第一條指令的邏輯地址。控制單元所執行的最後一步就是跳轉到中斷或異常處理程式。換句話說,處理完中斷訊號後,控制單元所執行的指令就是被選中處理程式的第一條指令。

上面的處理過程的描述摘自<<深入理解linux核心>>,其中有幾點值得注意的地方:

1:通過門後,只能提高執行級別。就像上面所述的 “當前特權級CPL(存放在cs暫存器的低兩位)與段描述符(存放在GDT中)的描述符特權級DPL比較。如果CPL小於DPL,就產生一個“通常保護”異常”。在中斷處理中,通常把IDT中的相應段選擇符設為__KERNEL_CS。即最高的執行級別
        2:上面C所述:“在新的棧中儲存ss和esp以前的值,這些值定義了與舊特權級相關的棧的邏輯地址”,那ss,esp以前的值是如何找到的呢?應該是從TSS中。在中斷髮生的時候,如果檢測到執行級別發生了改了,將暫存器SS,ESP中的值儲存進TSS的相應級別位置。再載入新的SS,ESP的值,然後從TSS中取出舊的SS,ESP值,再壓棧。
        3:堆疊的改變,如下圖所示:

從上圖中可以看到,硬體自動儲存的硬體環境是非常少,要在中斷後恢復到以前的環境,還需要儲存更多的暫存器值,這是由作業系統完成的。這在核心的程式碼中可以看到中斷和異常被處理完畢後,相應的處理程式必須產生一條iret指令,把控制權轉交給被中斷的程序,這將迫使控制單元:

1. 用儲存在棧中的值裝載cs、eip和eflag暫存器。如果一個硬體出錯碼曾被壓入棧中,並且在eip內容的上面,那麼,執行iret指令前必須先彈出這個硬體出錯碼。
        2. 檢查處理程式的CPL是否等於cs中的低兩位的值。如果是,iret終止返回;否則,轉入下一步。
        3. 從棧中轉載ss和esp暫存器,因此,返回到與舊特權級相關的棧。
        4. 檢查ds、es、fs及gs段暫存器的內容,如果其中一個暫存器包含的選擇符是一個段描述符,並且其DPL值小於CPL,那麼,清相關的段暫存器。控制單元這麼做是為了禁止使用者態的程式利用核心以前所用的段暫存器。如果不清除這些暫存器的話,惡意的使用者程式就會利用他們來訪問核心地址空間。

注意到4:舉例說明一下。如果通過系統呼叫進入核心態。然後將DS,ES的值賦為__KERNEL_DS(在2。4的核心裡),處理完後(呼叫iret後),恢復CS,EIP的值,此時CS的CPL是3。因為DS,ES被設為了__KERNEL_DS,所以其DPL是0,所以要將DS,ES中的值清除。在2。6核心中,發生中斷或異常後,將DS,ES的值設為了__USER_DS,避免了上述的清除過程,提高了效率。