天天看點

【CSAPP筆記】14. 異常控制流和程序

從給處理器加電,到斷電為止,處理器做的工作其實就是不斷地讀取并執行一條條指令。這些指令的序列就叫做 CPU 的控制流(control flow)。最簡單的控制流是“平滑的”,也就是相鄰的指令在存儲器中是相鄰的。當然,控制流不總是平滑的,不總是一條接一條地執行,總會有出現改變控制流的情況。我們知道的程式内部狀态改變的機制有兩條:

  • 跳轉和分支
  • 調用和傳回

這些機制局限于程式本身的控制。當系統狀态(system state)發生改變的時候,以上機制就不能很好地應對複雜的情況,例如:

  • 資料從磁盤或者網絡擴充卡到達
  • 有一條指令執行了除以零的操作
  • 使用者按下 ctrl+c
  • 系統内部的計時器到時間

現代系統通過使控制流發生突變來應對這些情況。這種機制叫做異常控制流(exceptional control flow)。異常控制流發生在計算機系統的各個層次。

【CSAPP筆記】14. 異常控制流和程式
  • 在硬體層,硬體檢測到的事件會觸發控制轉移到異常處理程式
  • 在作業系統層,核心通過上下文轉換将控制從一個程序轉到另一個程序
  • 在應用層,一個程序可以發送信号到另一個程序,接受者将控制突然轉移到一個信号處理程式

學習異常控制流很重要:

  • 可以幫助你了解重要的系統概念,例如 I/O、程序、虛拟存儲器
  • 可以幫助你了解應用程式和作業系統是如何互動的,例如陷阱、系統調用
  • 可以幫助你編寫程序相關的新應用程式,了解并發
  • 了解軟體異常如何工作

對于彙編語言、CPU、存儲器的學習,可能已經使你初步了解應用是如何與硬體互動的。而學習異常控制流的重要性在于開始學習應用是如何與作業系統進行互動的。

異常

最底層的機制稱為異常(Exception),更高層次的異常控制流包括程序切換(Process Context Switch)、信号(Signal)和非本地跳轉(Nonlocal Jumps)。

【CSAPP筆記】14. 異常控制流和程式

異常是異常控制流的一種形式,它是由硬體和作業系統組合來實作的。異常就是控制流的突變,用來響應處理器狀态的某種變化。狀态變化被稱為事件(event)。當處理器檢測到時間的發生時,它就會通過一張叫做異常表(exception table)的跳轉表,跳到一個專門用來處理這類事件的作業系統子程式——*異常處理程式(exception handler)

系統為每種類型的異常都配置設定了一個唯一的非負整數的異常号。當系統啟動時,作業系統配置設定并初始化異常表,條目 k 包含異常 k 的處理程式的位址,異常表的起始位址存放在異常表基址寄存器中。當處理器檢測到事件,并确定了異常号 k 後,處理器觸發異常,執行間接過程調用。異常類似于過程調用,但有些許不同:

  • 過程調用在跳轉之前,要把傳回位址壓入棧中。然而,根據異常的類型,傳回位址有可能是目前指令、下一條指令、也可能直接終止被中斷的程式。
  • 如果控制從使用者态轉到核心态,那麼這些項目被壓入核心棧而不是使用者棧。
  • 異常處理程式都運作在核心态下。

一旦硬體觸發了異常,剩下的工作就是由異常處理程式在軟體中完成。異常處理程式處理完事件之後,它通過執行特殊的“從中斷傳回”的指令,可選地傳回到被中斷的程式,将适當的狀态彈回到處理器的控制和寄存器中,如果中斷的是一個使用者程式,就将狀态恢複為使用者模式。最後将控制傳回給被中斷的程式。

異常的分類

異常分為四類:

類型 原因 同步or異步 傳回行為
中斷(interrupt) 來自 I/O 裝置的信号 異步 總是傳回到下一條指令
陷阱(trap) 有意的異常 同步
故障(fault) 潛在的可恢複錯誤 可能傳回到目前指令
終止(abort) 不可恢複的錯誤 不傳回

【CSAPP筆記】14. 異常控制流和程式

中斷是處理來自處理器外部的 I/O 裝置的信号的結果。中斷是異步(asynchronous)的,異步的意思就是是由處理器外面發生的事情引起的。對于執行程式來說,這種“中斷”的發生完全是不知道什麼時候會發生,CPU對其的響應也完全是被動的。

如下的例子就是中斷:

  • 計時器中斷:計時器中斷是由計時器晶片每隔幾毫秒觸發的,核心用計時器中斷來從使用者程式手上拿回控制權。
  • I/O 裝置的中斷,這個種類就包含很多可能了。例如:使用者按下 Ctrl+C、網絡上一個資料包的到達、磁盤上的資料到達等等。

通過像處理器晶片上的一個引腳發信号,将異常号放到總線上,觸發中斷。異常号辨別引起中斷的裝置。處理器意識到中斷後,調用适當的中斷處理程式。當處理程式傳回時,将控制傳回給下一條指令。就好像程式正常地執行,沒有發生過中斷一樣。

除了中斷的其餘三者(陷阱、故障和終止)是同步(synchronous)的,意思是執行目前指令的時候觸發異常。

陷阱是有意的異常。陷阱是一類很重要的異常。有一個用途就是在使用者程式和核心(kernel)之間提供一個接口,叫做系統調用(system call)。

【CSAPP筆記】14. 異常控制流和程式
【CSAPP筆記】14. 異常控制流和程式

系統調用看起來就像函數調用。通常,處理器設有兩種模式:“使用者模式”與“核心模式”,

然而某些功能是需要系統核心級别的支援才能完成,是以核心提供一系列具備預定功能的函數,通過一組稱為系統調用的接口呈現給使用者。系統調用把應用程式的請求、參數和控制傳給核心,調用相應的的核心函數完成所需的處理,将處理結果傳回給應用程式。

使用者模式和核心模式

為了使作業系統核心得到保護,作業系統提供一個機制,限制應用程式可以使用的指令以及它可以通路的位址空間範圍。處理器通常是使用某個控制寄存器中的一個模式位(mode bit)來提供這種功能的。當設定了模式位,程序就運作在核心狀态,可以執行指令集中的任何指令,通路任何存儲器位置。相反,則運作在使用者模式,不允許執行一系列特權指令,例如停止處理器、改變模式位、或者發起一個 I/O 操作,同時限制使用者模式中的程序通路核心區的代碼和資料,任何這樣的嘗試都會導緻故障。程序從使用者模式變為核心模式的方法就是通過注入中斷、故障或陷入系統調用這樣的異常。

故障就是由錯誤情況引起的,它可能被故障處理程式修正,修正之後就可以将控制傳回到引起故障的指令,并重新處理它。當然,也有可能是一個無法修複的故障,那麼處理程式會傳回到核心中的 abort 例程,會終止引起故障的程式。

故障的示例:

【CSAPP筆記】14. 異常控制流和程式

一、缺頁(page fault),當指令引用一個虛拟位址,而虛拟位址對應的實體頁面此時不在主存而在硬碟中,會發生缺頁故障。缺頁處理程式就從磁盤加載适當的頁(一個頁面就是一個虛拟存儲器的連續的一個塊,典型的是 4KB),然後将控制傳回給引起故障的指令。因為相應的頁面排程已經完成,在此執行指令時,就不會有缺頁故障了。

【CSAPP筆記】14. 異常控制流和程式

二、非法的存儲器位址引用(invalid memory reference)也是一種故障,隻不過故障處理程式會将控制傳回給 abort 例程,abort 例程會終止這個程式,并報“段錯誤”。

終止是不可恢複的緻命錯誤造成的結果,通常是一些硬體錯誤。終止處理程式從來不将控制傳回給應用程式,而是傳回給 abort 例程,終止這個應用程式。

從上面的例子中,我們可以看到異常的具體實作是依靠在使用者代碼和核心代碼間切換而實作的,是比彙編中的跳轉、傳回更加底層的機制。

程序

異常是允許作業系統提供程序(process)概念的基本構造塊。程序是計算機科學中最深刻最成功的概念之一。程序為每個程式提供一種假象,好像這個程式是計算機上唯一運作的程式,并獨占處理器和存儲器資源。而實際上在任何時間點,計算機上都有多個程式在運作。這種假象就是通過程序的概念提供給我們的。

程序的定義是:

A process is an instance of a running program.

程序就是計算機中一個執行中的程式的執行個體。程式本身隻是指令、資料及其組織形式的描述,程序才是程式(那些指令和資料)的真正運作執行個體。

使用者下達運作程式的指令後,就會産生程序。作業系統如何實作程序、管理程序的細節超出了本書的讨論範圍,我們将關注程序這個概念提供給程式的兩種關鍵抽象:

  • 邏輯控制流(logic control flow),提供一個假象——好像程式獨占處理器。
  • 私有位址空間,是通過虛拟記憶體(virtual memory)的機制,提供一個假象——好像程式獨占存儲器。

邏輯控制流

多程序的操作是怎樣的呢?

【CSAPP筆記】14. 異常控制流和程式

現代處理器一般有多個核心,是以可以真正同時執行多個程序。而對每一個核心來說,還是有可能會切換執行不同的程序。在上圖的多程序模型中,虛線部分可以認為是目前正在執行的程序,記憶體中需要另一塊區域,當處理器去執行别的程序時,來儲存目前的寄存器值。

【CSAPP筆記】14. 異常控制流和程式

如果用調試器單步執行程式,我們會看到一系列的程式計數器的值,值的序列就是邏輯控制流,每一個程序都有自己的邏輯流。在上圖中,每條豎線都代表一部分邏輯流。多個程序輪流進行的概念叫做多任務(multitasking),作業系統中常用時間片(time slice)的機制來實作,是以多任務又稱為時間分片(time slicing)。邏輯流如果在執行時間上與另一個邏輯流重疊,那麼就叫并發流(concurrent flow),這兩個流就是并發地執行(concurrency),否則叫做順序流(sequential flow)。并發與處理器核心數無關,隻需要滿足兩個流在時間上重疊,那就是并發執行。如果兩個流并發地運作在不同的處理器核心上,那麼就稱為并行流(parallel flow),并行流是并發流的真子集。

上下文切換

【CSAPP筆記】14. 異常控制流和程式

切換程序時,核心會負責具體的排程,用到的機制稱為上下文切換(context Switch)。上下文(context)是核心重新開機一個被搶占的程序所需要的狀态,也可以了解為一個程序擁有的資訊。包括代碼、資料、堆棧、通用寄存器的内容、程式計數器、環境變量、核心資料結構,比如打開檔案描述符的集合、描繪位址空間的頁表、包含程序資訊的程序控制塊等等。

上下文切換是一種較高形式的異常控制流。在程序某些時刻,核心可以決定搶占目前執行的程序,重新開始一個被搶占的程序,這種行為叫做排程(schedule)。核心的做法是:

  1. 儲存目前程序的上下文,
  2. 恢複某個先前被搶占的程序的上下文,
  3. 将控制傳遞給這個新恢複的程序。

所有的系統都有某種産生周期性定時器中斷的機制,典型的就是每 1 毫秒或每 10 毫秒,核心判定某個程序已經運作了足夠長的時間,要切換到另一個程序運作。

程序位址空間

【CSAPP筆記】14. 異常控制流和程式

在邏輯控制流中我們可以看到,整個過程中,CPU 交替執行不同的程序,而每個程序的記憶體大緻都長一個樣子。虛拟記憶體系統會負責管理位址空間,程序也給程式提供一種假象,好像它獨占使用系統的位址空間。

參考連結

  • wiki
  • 程序相關函數簡單總結
  • 15213課程網站