上下文儲存 中斷 IA 32的中斷和異常處理

2021-10-16 07:14:04 字數 4127 閱讀 2718

在os的內容中,我們很多次說到中斷和異常,不過都沒有詳細的介紹,這裡會有乙個詳細的介紹。

1.什麼是中斷和異常(interrupt and exception),它們在什麼場景下產生?

你在圖書館看書,突然乙個**來了,說你家狗丟了,那麼你肯定要去找狗。可能你很快找到了,回去繼續看書,也有可能沒有那麼容易找到,你就不回去看書了。這個比如對應到計算機中,你是處理器,你家狗丟了這個通知就是中斷和異常的訊號,你去找狗就是中斷和異常的處理。你回去繼續看書,就是中斷和異常處理完成後繼續執行之前的任務,不能回去看書,就是一些中斷和異常會導致使用者程式被os kill。

每種型別的中斷和異常,我們都會進行編號,稱為vector number。處理器支援從0到255的編號,最多256種情況。其中前32個是cpu自己用的,剩下的交給開發者。下面的表6-1顯示對應編號的具體中斷和異常型別:

那麼在計算機中,中斷和異常在哪些情況下會產生呢?

1.中斷:

1.外部的硬體產生(比如有乙個網路i/o來了)。cpu有對應的引腳來接受中斷資訊,分別為intr和nmi。從intr通知的中斷訊號是可以遮蔽的,通過eflags中的if標誌來控制。nmi的中斷必須處理,並且nmi對應的中斷號預設是2.

2.程式自己通過呼叫int指令產生。比如int 35 就表示強制執行35號中斷的處理程式。

2.異常

1.指令執行的時候產生的錯誤。對於每種錯誤處理器都會有相應的編號。異常通常被分為這三種不同的型別:faults,traps和aborts

2.使用into, int 3, 和 bound命令產生。不過使用int n指令來產生指令是有限制的。如果int n指令指定的vertor number是處理器定義的異常。這種情況如果是硬體正常產生通常是會有錯誤**的,但是如果是指令產生這種異常,處理器在處理的時候是不會將錯誤**進行壓棧的。這樣在進行異常處理的時候,異常處理程式仍然會去對錯誤**進行出棧操作,因為沒有錯誤**,所以pop的時候就會將eip設定為錯誤資料,這樣返回的時候執行點就不是之前的了。

3.機器檢查的異常。p6和pentium系列的處理器提供了內部和外部的機器檢查機制來檢查內部晶元硬體的執行和匯流排事務。如果這種檢查發現了錯誤,那麼處理器會發起乙個vector 18的機器檢查異常並且返回乙個錯誤**。

上面說到異常會被分為faults,traps,aborts.這是根據這些異常產生的方式和異常處理後重新開始執行指令的地點來分類的。

不可遮蔽的中斷nmi (nonmaskable interrupt)

nmi可以通過下面2種方式產生:

不管是這2種中的哪一種,處理器都會立刻呼叫nmi中斷對應的中斷處理程式。並且在處理程式執行期間保證沒有其他的中斷產生,包括nmi中斷。當然,這2種方式產生的nmi中斷不會被eflags中的if標誌位遮蔽。

還有一種方式,也就是通過intr引腳產生可遮蔽中斷,不過中斷的vector number是2。這種模擬的方式並不是真正的nim中斷,所以在進行相應處理的時候,處理器對應的專門用來支援nmi中斷的硬體並不會工作,

當nmi中斷在處理的過程中,處理器會阻塞其他的nmi中斷直到中斷處理程式的iret呼叫完成(也就是說中斷處理程式已經執行完成了)。所以可以看出nmi中斷處理程式是不允許巢狀執行的。另外,如果iret指令執行的時候產生了乙個錯誤,是不會影響nmi中斷被開啟的。

eflags中的if標記位可以用來開啟或者遮蔽intr引腳的中斷,不過不會影響nmi中斷的接受,處理器產生的異常也不會受到影響。可以分別使用sti(set interrupt-enable flag )和cli (clear interrupt-enable flag) 來設定和清除if標誌位。下面的三種情況也會對if標誌位產生影響。

當我們在切換棧的時候,可能會使用如下的2個指令來完成操作:

mov ss, ax

mov esp, stacktop

但是問題在於,如果第一條指令執行完成後,發生了中斷或者異常,那麼在中斷或者異常的處理程式執行的過程中,對應的棧的位址空間就是異常的了。為了解決這個問題,處理器在修改ss暫存器的命令後禁止中斷,debug異常,single-step trap ,直到下一條指令執行完畢。當然如果使用lss命令來修改ss暫存器的值就不會出現這個問題了。

中斷和異常的優先順序

如果有多個中斷或者異常被掛起,處理器會按照乙個先後順序來進行處理。下面的表6-2給出了各種異常和中斷的優先順序。

上面表中的分類等級在各個架構中都是統一的,不過在各個分類中不同的中斷或者異常的優先順序各個架構就會不一樣了。處理器會首先處理最高等級的異常或者中斷。低等級別的異常會被丟掉,低等級別的中斷會被掛起。當中斷處理程式返回到程式或者任務產生異常或者中斷的地方,被丟掉的異常會再次產生,中斷描述符表idt( interrupt descriptor table )

接收到中斷後,處理器應該怎麼處理呢?首先每種型別的中斷和異常對應的處理程式可以在idt中找到。和gdt和ldt型別,idt也是乙個陣列,其中的元素就是8byte大小的門描述符(gate descriptor)。你可以把idt放在任何位置,然後將對應的線性位址基位址記載idtr暫存器中就可以了,下面的圖6-1展示了2者的關係和應用。

idt中包含3中型別的門描述符:

1.任務門描述符 task-gate descriptor

2.中斷門描述符 interrupt-gate descriptor

3.陷阱門描述符 trap-gate descriptor

下面的圖6-2顯示了各種描述符的內容格式:

下面我們來看下具體的中斷和異常處理的過程。

處理器對異常和中斷處理程式的呼叫,類似於使用call指令呼叫乙個過程或者任務一樣。首先根據中斷或者異常編號在idt中找到對應的描述符。如果是trap-gate 或者interrupt-gate,那麼就類似於使用call命令呼叫乙個過程。如果是task-gate,則會進行上下文切換,執行task-gate對應的任務,類似於call呼叫乙個task gate。圖6-3顯示了這個過程:

如果中斷和異常處理過程的許可權和目前**的許可權不同:

1.從當前任務的tss中獲取相應許可權的棧資訊,在新的棧上,處理器將被中斷的過程的段選擇子和棧指標資訊入棧

2.處理器將當前過程的eflags, cs, 和 eip 暫存器的值入棧

3.如果有錯誤**,那麼也入棧

如果相同:

1.將eflags, cs, 和 eip 暫存器的值入棧

2.如果有錯誤**也入棧

下面的圖6-4顯示了這2種不同情況:

不管是通過interrupt gate還是trap gate來訪問異常或者中斷的處理程式,處理器都會在儲存了eflags 暫存器值後將tf標誌位清空。清除 tf標誌位可以防止指令調製影響中斷的響應。 iret指令會將tf的值重新恢復。

interrupt gate還是trap gate唯一區別在於對於if標誌位的使用。interrupt gate在呼叫的時候會清除if標誌位,但是trp gate不會。

如果異常或者中斷的處理程式是task gate,那麼在進行異常或者中斷處理的時候會產生任務的切換,下面是使用任務來作為中斷或者異常處理的優勢:

被中斷的過程或者任務的上下文會被自動儲存

新的任務有新的棧可以使用,可以防止原有棧的問題導致系統出現故障

可以定義新的位址空間,做到任務的分離。

當然劣勢也很明顯,就是任務切換太耗費資源,所以效率很慢。

下面的圖6-5是呼叫過程的圖示。

linux中斷 程序上下文和中斷上下文

中斷發生以後,cpu跳到核心設定好的中斷處理 中去,由這部分核心 來處理中斷。這個處理過程中的上下文就是中斷上下文。為什麼可能導致睡眠的函式都不能在中斷上下文中使用呢?首先睡眠的含義是將程序置於 睡眠 狀態,在這個狀態的程序不能被排程執行。然後,在一定的時機,這個程序可能會被重新置為 執行 狀態,從...

上下文儲存 中斷 深入理解CPU上下文切換

我們都知道cpu上下文切換,會增加系統負載。那什麼是cpu上下文,為什麼要切換?我們都知道,linux 是乙個多工作業系統,它支援遠大於 cpu 數量的任務同時執行。當然,這些任務實際上並不是真的在同時執行,而是因為系統在很短的時間內,將 cpu 輪流分配給它們,造成多工同時執行的錯覺。而在每個任務...

程序上下文和中斷上下文

程序上下文和中斷上下文是作業系統中很重要的兩個概念,這兩個概念在作業系統課程中不斷被提及,是最經常接觸 看上去很懂但又說不清楚到底怎麼回事。造成這種局面的原因,可能是原來接觸到的作業系統課程的教學總停留在一種淺層次的理論層面上,沒有深入去研究。處理器總處於以下狀態中的一種 核心態,執行於程序上下文,...