中斷和異常——內核的動力

本文內容關注一種內核工作的基本機制——中斷和異常。標題稱這種機制爲內核的動力,或許不那麼恰當,主要想強調中斷和異常對內核工作的重要性。對於操作系統上層的應用程序開發者而言,或許感覺離中斷和異常的機制很遠,因爲我們不需要開發內核。但是畢竟我們使用的是內核所提供的各種服務,理解內核的基本運作原理能夠幫助我們更好地寫程序、優化程序。

問在前面:

  • 通常所說的“陷入內核”,是怎麼個陷入法呢?
  • 我們的網卡收到數據,內核怎麼知道該去處理數據了呢?時刻去輪詢一下,恐怕不太現實?
  • 我們訪問了一個非法的內存地址,或者執行了一個除0操作。毫無懸念,內核崩潰。那麼內核是怎麼知道進程崩潰了,該來收拾這個爛攤子了?

直觀來看,內核是什麼?

內核地址空間和進程的地址空間相互獨立?

我們都知道,Linux內核與進程的地址空間相互獨立。這是什麼意思?直觀的比喻就是,每個進程在自己的內存區域工作,內核也在自己的區域工作,它們之間互相都是不可見的。(這點由虛擬內存管理機制保證)。在同一個地址空間內部,我們可以簡單的使用函數調用(或者說過程調用)來使用別的函數提供的功能。比如,你自己寫的程序,函數funcA調用funcB。又比如,在Linux內核內部,函數可以直接調用。但是在兩個地址空間之間,交互就沒那麼容易了。比如,如果進程想要使用內核提供的服務(文件讀寫,網絡通信等等),怎麼做呢?答案是,系統調用(陷入內核)。

系統調用怎麼實現呢?

系統調用的目的就是進程能夠使用內核的服務。但是具體是怎麼向內核發起調用(請求)的呢。答案是中斷。我們知道,CPU不斷的取指令,執行指令。我們跟CPU約定好,如果遇到一種指令,叫“軟中斷指令”,CPU就保存當前進程的上下文,改變CPU的模式,然後跳轉到內核的中斷向量表,獲取“軟中斷處理程序”的地址,然後執行。這就算陷入內核了。

內核是由中斷驅動的一個程序實體

內核由各種各樣的服務組件組成,各種系統調用、文件系統、網絡系統、調度子系統等等。各種各樣的內核活動的產生並不是憑空的,都是需要某種方式驅動,這就是中斷。調度:定時器按一個時間片的週期觸發一箇中斷,內核由此觸發一次調度過程。系統調用:進程觸發一個軟中斷,內核由此執行一個具體的系統服務。設備驅動:網卡收到數據,觸發一次中斷,內核由此執行一次對網卡的讀操作。等等。

中斷和異常

本質上,中斷和異常屬於同一個範疇。

我們知道,CPU的工作就是不斷的取指令,執行指令。每執行一次指令,或者在執行指令期間會發生三種情況:

    1. 指令按預想的結果執行,然後取下一條指令,繼續執行。
    1. 這是一條特殊指令,預示一種異常的流向。比如除0操作,越界訪問,缺頁異常或者系統調用的軟中斷指令。
    1. 執行期間,外部設備觸發了一箇中斷,而CPU不得不響應這個中斷,轉去執行中斷服務程序。這是另一種類型的異常流向。

對於第2種情況,我們稱之爲異常。就是CPU執行指令過程中出現的異常情況。因爲它是CPU執行完一條指令之後觸發的,所以也稱爲“同步中斷”。對於第3種情況,稱爲“中斷”,CPU隨時都有可能收到外部設備的中斷,具有隨機性,也稱之爲“異步中斷”。

不管是中斷還是異常,它們的處理程序都是內核來執行的。

中斷上下文與進程上下文

首先談進程上下文:

示例:

同樣是一次read調用,進程A和進程B得到效果是不一樣的,進程A讀取文件a,獲取到x個字節;進程B讀取文件b,獲取到y個字節。而內核肯定只有一個read調用。
內核如何做到區別對待?

只告訴內核執行什麼系統調用是不夠的,內核必須知道,爲哪個進程服務,讀取哪個文件,預期讀取多少個字節,要拷貝用戶緩衝的目的地址等等。簡言之,內核需要一個執行環境,這個環境就是所謂的“進程上下文”。即,當一個進程正在進行時,CPU所有寄存器中的值、進程的狀態以及堆棧中的內容稱之爲進程的上下文。所以,進程執行系統調用,“內核代表進程執行,在進程上下文”。

再來就是中斷上下文,和進程上下文是一個道理,當硬件觸發中斷的時候,內核需要獲取到當前該硬件的各種變量和參數,內核根據這些參數來執行中斷服務程序。而硬件傳過來的各種參數和內核需要保存的一些環境(被中斷的進程環境),可以看作是中斷上下文

總結

總的來說,內核就是一堆處理過程合成的實體。它基本沒有主動性,靠的是各種中斷來驅動的。進程有需求了,觸發中斷告訴它;程序執行出錯了,觸發中斷告訴它;設備狀態變化了,還是觸發中斷告訴它;時間片到期,該調度了,觸發中斷告訴它

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章