[CSAPP筆記][第八章異常控制流程]

來源:互聯網
上載者:User

標籤:

異常控制流程

  • 控制轉移
  • 控制流程

系統必須能對系統狀態的變化做出反應,這些系統狀態不是被內部程式變數捕獲,也不一定和程式的執行相關。

現代系統通過使控制流程 發生突變對這些情況做出反應。我們稱這種突變異常控制流程( Exceptional Control Flow,ECF)

異常控制流程發生在系統的各個層次。

理解ECF很重要

  • 理解ECF將協助你理解重要的系統概念。
  • 理解ECF將協助你理解應用程式如何與作業系統互動
    • 通過陷阱(trap)或者系統調用(system call)的ECF形式,向作業系統請求服務。
  • 理解ECF將協助你編寫有趣的應用程式
  • 理解ECF將協助你理解並發
  • 理解ECF將協助你理解軟體異常如何工作。

這一章你將理解如何與作業系統互動,這些互動都圍繞ECF

8.1 異常

異常異常控制流程的一種,一部分由硬體實現,一部分由作業系統實現。

  • 異常(exception)就是控制流程的突變,用來響應處理器狀態的某些變化。
  • 狀態變化又叫做事件(event)

    • 事件可能與當前執行指令有關
      • 儲存空間缺頁,算數溢出
      • 除0
    • 也可能與當前執行指令無關
      • I/O請求
      • 定時器產生訊號
  • 通過異常表(exception table)的跳轉表,進行一個間接程序呼叫,到專門設計處理這種事件的作業系統子程式(例外處理常式(exception handler))

  • 異常處理完成後,根據事件類型,會有三種情況

    • 返回當前指令,即發生事件時的指令。
    • 返回沒有異常,所執行的下一條指令
    • 終止被中斷的程式
8.1.1 異常處理
  • 為每個異常分配了一個非負的異常號(exception number)

    • 一些號碼由處理器設計者分配
    • 其他號碼由作業系統核心的設計者分配。
  • 系統啟動時,作業系統分配和初始化一張稱為異常表的跳轉表。

    • 條目k包含異常k的處理常式的地址。
  • 異常表的地址放在叫異常表基底位址暫存器的特殊CPU寄存器中。)

  • 異常類似程序呼叫,不過有以下不同

    • 程序呼叫,跳轉到處理常式前,處理器將返回地址壓入棧中。對於異常,返回地址是當前,或下一跳指令。
    • 處理器會把額外的處理器狀態壓入棧中。
    • 如果控制一個使用者程式到核心,那麼所有這些項目會被壓入核心棧中,而是使用者棧。
    • 例外處理常式運行在核心模式下,這意味他們對所有系統資源有完整存取權限。
8.1.2 異常的類別

異常分為一下四類:中斷(interrupt),陷阱(trap),故障(fault)和終止(abort)。

  1. 中斷

    • 中斷非同步發生,是來自處理器外部的I/O裝置的訊號的結果。硬體中斷不是由任何一條專門的指令造成,從這個意義上它是非同步。
    • 硬體中斷的例外處理常式通常稱為中斷處理常式(interrupt handle)
      • I/O裝置通過向處理器晶片的一個引腳發訊號,並將異常號放到系統匯流排上,以觸發中斷。
      • 在當前指令執行完後,處理器注意到中斷引腳的電壓變化,從系統匯流排讀取異常號,調用適當的中斷處理常式。
      • 當處理常式完成後,它將控制返回給下一條本來要執行的指令。

      • 剩下的異常類型(陷阱,故障,終止)是同步發生,執行當前指令的結果。我們把這類指令叫做故障指令(faulting instruction).
  2. 陷阱和系統調用

    • 陷阱有意的異常,是執行一個指令的結果。也會返回到下一跳本來要執行的指令。
    • 陷阱最重要的用途是在使用者程式和核心之間提供一個像過程一樣的介面,叫做系統調用
      • 使用者程式經常需要向核心請求服務。
        • 讀檔案(read)
        • 建立進程(fork)
        • 新的程式(execve)
        • 終止當前進程(exit)
      • 為了運行對這些核心服務的受控訪問,處理器提供了一條特殊的syscall n的指令
      • 系統調用是運行在核心模式下,而普通調用是使用者模式下。
  3. 故障

    • 故障由錯誤引起,可能被故障處理常式修正。

      • 如果能被修正,返回引起故障的指令。
      • 否則返回abort常式,進行終結。
  4. 終止

    • 終止是不可恢複的致命錯誤造成的結果,通常是一些硬體錯誤,比如DRAM和SRAM被損壞。
    • 終止處理常式從不將控制返回給應用程式。返回一個abort常式。

8.1.3 Linux/IA32 系統中的異常
  • 有高達256種不同的異常

    • 0~31 由Intel架構師定義的異常,對任何IA32系統都一樣。
    • 23~255 對應作業系統定義的中斷和陷阱。

      1. Linux/IA32 故障和終止
    • 除法錯誤

    • 一般保護故障
    • 缺頁
    • 機器檢查

      1. Linux/IA32 系統調用

  • 在IA32系統中,系統調用是通過一條稱為int n的陷阱指令完成,其中n可能是IA32異常表256個條目中任何一個索引,曆史中,系統調用是通過異常128(0x80)提供的。

  • C程式可用syscall函數來直接調用任何系統調用

    • 實際上沒必要這麼做
    • C庫提供了一套方便的封裝函數。這些封裝函數將參數打包到一起,以適當的系統調用號陷入核心,然後將系統調用的返回狀態傳遞迴調用函數。
    • 我們將系統調用與他們相關聯的封裝函數稱為系統級函數

研究程式如何使用int指令直接調用Linux 系統調用是很有趣的。所有到Linux系統調用的參數都是通過通用寄存器而不是棧傳遞。

慣例

  • %eax 包含系統調用號
  • %ebx,%ecx,%edx,%esi,%edi,%ebp包含六個任意的參數。
  • %esp不能使用,進入核心模式後,核心會覆蓋它。
  • 系統級函數寫的hello world

    int main(){    write(1,"hello,world\n",13);    exit(0);}
  • 彙編寫的hello world

    string:        "hello world\n"main:        movl $4,%eax        movl $1,%ebx        movl $String,%ecx        movl $len,%edx        int $0x80        movl $1,%eax        movl $0,%ebx        int $0x80

8.2 進程
  • 異常是允許作業系統提供進程的概念的基本構造快,進程是電腦科學中最深刻,最成功的概念之一。
    • 假象,覺得我們的程式是系統中唯一運行著的程式。我們的程式好像獨佔處理器和儲存空間。
    • 這些假象都是通過進程概念提供給我們的。
  • 進程經典定義:一個執行中的程式執行個體.
    • 系統中每個程式都是運行某個進程的上下文中的。
      • 上下文是由程式正確運行所需的狀態組成。
      • 這個狀態包括儲存空間中的代碼和資料,它的棧,通用目的寄存器,程式計數器,環境變數等。
  • 進程提供的假象
    • 一個獨立的邏輯控制流程
    • 一個私人的地址空間
8.2.1 邏輯控制流程
  • PC值的序列叫做邏輯控制流程,或者簡稱邏輯流
8.2.2 並發流
  • 邏輯流也有不同的形式。

    • 例外處理常式,進程,訊號處理常式,線程和Java進程都是邏輯流的例子。
  • 一個邏輯流的執行在執行上與另一個流重疊,稱為並發流,這兩個流被稱為並發地運行

    • 更準確地說,流X和Y互相併發。
  • 多個流並發執行的一般現象稱為並發

    • 一個進程和其他進程輪流執行的概念稱為多任務
    • 一個進程執行它的控制流程的一部分的每一時間段叫做時間片
    • 因此,多任務 又叫時間分區
  • 並發的思想與流程執行的處理器核心數與電腦數無關。

    • 如果兩個流在時間上重疊,即使運行在同一處理器,也是並發。
    • 並行流是並發流的一個真子集。
      • 兩個流並發地運行在不同的處理器核或者電腦上,我們稱為並行流
      • 它們並行地運行,且並行地執行

你吃飯吃到一半,電話來了,你一直到吃完了以後才去接,這就說明你不支援並發也不支援並行

你吃飯吃到一半,電話來了,你停了下來接了電話,接完後繼續吃飯,這說明你支援並發

你吃飯吃到一半,電話來了,你一邊打電話一邊吃飯,這說明你支援並行

並發的關鍵是你有處理多個任務的能力,不一定要同時。

並行的關鍵是你有同時處理多個任務的能力。

8.2.3 私人地址空間

進程 為個程式好像獨佔了系統地址空間。

  • 一個進程為每個程式提供它自己的私人地址空間
  • 不同系統一般都用相同的結構。

8.2.4 使用者模式和核心模式

處理器提供一種機制,限制一個應用程式可以執行的指令以及它可以訪問的地址空間範圍。這就是使用者模式核心模式

  • 處理器通過控制寄存器中的一個模式位來提供這個功能。

    • 該寄存器描述了進程當前享有的特權。
      • 設定了模式位後,進程就運行在核心模式中(有時也叫超級使用者模式)
        • 核心模式下的進程可以執行指令集的任何指令,訪問系統所有儲存空間的位置。
      • 沒有設定模式位時,進程運行在使用者模式。
        • 使用者模式不允許程式執行特權指令。
          • 比如停止處理器,改變模式位,發起一個I/O操作。
        • 不允許使用者模式的進程直接引用地址空間的核心區代碼和資料。
        • 任何嘗試都會導致保護故障
        • 使用者通過系統調用間接訪問核心代碼和資料。
    • 進程從使用者模式轉變位核心模式的方法
      • 通過中斷,故障,陷入系統調用這樣的異常。
      • 在例外處理常式中會進入核心模式。退出後,又返回使用者模式。
  • Linux提供一種聰明的機制,叫/proc檔案系統。

    • 允許使用者模式訪問核心資料結構的內容。
    • /proc檔案將許多核心資料結構輸出為一個使用者程式可以讀的文字檔的階層。
      • 如CPU類型(/proc/cpuinfo)
      • 特殊進程使用的儲存空間段(‘/proc//maps’)
    • 2.6 版本引入Linux核心引入/sys檔案系統。
      • 輸出關於系統匯流排和裝置的額外的底層資訊。
8.2.5 環境切換作業系統核心使用一種稱為 環境切換的 較高層次 的 異常控制流程來實現多任務。
  • 環境切換機制建立在之前討論的較低層次異常機制上的。
核心為每個進程維護一個 上下文
  • 上下文就是重新啟動一個被搶佔的進程所需的狀態。
    • 由一些對象的值組成
      • 通用目的寄存器
      • 浮點寄存器
      • 程式計數器(PC)
      • 使用者棧
      • 狀態寄存器
      • 核心棧
      • 各種核心資料結構
        • 描繪地址空間的頁表
        • 包含當前進城資訊的進程表
        • 進程已開啟檔案資訊的檔案表
  • 在進程執行的某些時刻,核心可以決定搶佔當前進程,並重新開始一個先前被搶佔的進程。這種決定叫做調度(shedule),由核心中稱為調度器(scheduler)的代碼處理的。

    • 當核心選擇一個新的進程運行時,我們就說核心調度了這個進程。
  • 當調度進程時,使用一種環境切換的機制來控制轉移到新的進程

    • 儲存當前進程的上下文
    • 恢複某個先前被搶佔的進程被儲存的上下文
    • 將控制傳遞給這個新恢複的進程
  • 什麼時候會發生環境切換
    • 核心代表使用者執行系統調用
      • 如果系統調用因為某個事件阻塞,那麼核心可以讓當前進程休眠,切換另一個進程。
      • 或者可以用sleep系統調用,顯式請求讓調用進程休眠。
      • 即使系統調用沒有阻塞,核心可以決定執行內容切換
    • 中斷也可能引發環境切換。
      • 所有系統都有某種產生周期性定時器中斷的機制,典型為1ms,或10ms。
      • 每次定時器中斷,核心就能判斷當前進程運行了足夠長的時間,切換新的進程

快取汙染和異常控制流程

一般而言,硬體快取儲存空間不能和諸如中斷和環境切換這樣的異常控制流程很好地互動,如果當前進程被一個中斷暫時中斷,那麼對於中斷處理常式來說快取器是冷的。如果處理常式從主存訪問足夠多的表項,被中斷的進程繼續的時候,快取對於它來說也是冷的,我們稱中斷處理常式汙染了快取。使用 環境切換也會發生類似的現象。

8.3 系統調用錯誤處理
  • 當Unix系統級函數遇到錯誤時,他們典型地返回-1,並設定全域變數errno來表示什麼出錯了。

    if((pid=fork()<0){        fprintf(stderr,"fork error: %s\n", strerror(errno));        exit(0);}
  • strerror 函數返回一個文本串,描述了個某個errno值相關聯的錯誤。
8.4 進程式控制制8.4.1 擷取進程ID
#include<sys/types.h>#include<unistd.h>pid_t getpid(void);pid_t getppid(void);
  • PID是每個進程唯一的正數。
  • getpid()返回調用進程的PID,getppid()返回它的父進程的PID。
  • 返回一個類型pid_t的值,在Linux系統下在type.h被定義為int
8.4.2 建立和終止進程

進程總是處於下面三種狀態

  • 運行。進程要麼在CPU中執行,要麼等待執行,最終被核心調度。
  • 停止。進程的執行被掛起,且不會被調度。

    • 收到SIGSTOP,SIGTSTP,SIDTTIN或者SIGTTOU訊號,進程就會停止。
    • 直到收到一個SIGCONT訊號,在這個時刻,進程再次開始運行。
    • 訊號是一種軟體中斷的形式。
  • 終止。進程永遠停止。

    • 收到一個訊號。訊號預設行為是終止進程。
    • 從主程式返回
    • 調用exit函數
      • exit函數以status退出狀態來終止進程(另一種設定方式在main中return )
子進程

父進程通過調用fork函數建立一個新的運行子進程

#include<sys/types.h>#include<unistd.h>pid_t fork(void);返回:子進程返回0,父進程返回子進程的PID,如果出錯,返回-1;

新建立的子進程幾乎但不完全與父進程相同。

  • 子進程得到與父進程使用者級虛擬位址空間相同的(但是獨立的)一份拷貝。

    • 包括文本,資料和bss段,堆以及使用者棧。子進程還獲得與父進程任何開啟檔案描述符相同的拷貝。
    • 意味著當父進程調用fork時,子進程可以讀寫父進程中開啟的任何檔案。
    • 父進程和新建立的子進程之間最大的區別在於有不同的PID 。
  • fork()函數會第一次調用,返回兩次,一次在父進程,一次在子進程。

    • 傳回值用來明確是在父進程還是在子進程中執行。

  • 調用一次,返回兩次
    • 對於具有多個fork執行個體的需要仔細推敲了
  • 並發執行

    • 父進程和子進程是並發啟動並執行獨立進程。
    • 核心可能以任意方式覺得執行他們的順序。
    • 不能對不同進程中指令的交替執行做任何假設。
  • 相同但是獨立的地址空間

    • 在剛調用時,幾乎什麼都是相同的。
    • 但是它們都有自己的私人空間,之後對x的改變是相互獨立的。
  • 共用檔案

    • 父進程和子進程都把他們的輸出顯示在螢幕上。
    • 子進程繼承了父進程所有開啟的檔案。

畫進程圖會有協助。

8.4.3 回收子進程

當一個進程由於某種原因終止時,核心並不是立即把它從系統中清除。相反,進程被保持在一種已終結的狀態,知道被它的父進程 回收(reap)。

當父進程回收已終止的子進程時,核心將子進程的退出狀態傳遞給父進程,然後拋棄已終止的進程。

一個終止了但還未被回收的進程叫做僵死進程

如果父進程沒有回收,而終止了,那麼核心安排init進程來回收它們。

  • init進程的的PID位1,在系統初始化時由核心建立的。
  • 長時間啟動並執行程式,如shell,伺服器,總是應該回收他們的僵死子進程

一個進程可以通過調用waitpid函數來等待它的子進程終止或停止

#include<sys/types.h>#include<sys/wait.h>pid_t waitpid(pid_t pid ,int *status, int options);返回:如果成功,則為子進程的PID,如果WNOHANG,則為0,如果其他錯誤,則為-1.

waitpid函數有點複雜。預設(option=0)時,waitpid掛起調用進程的執行,知道它的等待集合中的一個子進程終止,如果等待集合的一個子進程在調用時刻就已經終止,那麼waitpid立即返回。在這兩種情況下,waitpid返回導致waitpid返回的已終止子進程的PID,並且將這個已終止的子進程從系統中去除。

  • 判斷等待集合的成員

    等待集合的成員通過參數pid確定

    • 如果pid>0,那麼等待集合就是一個獨立的子進程,它的進程ID等於PID
    • 如果pid=-1,那麼等待集合就是由父進程所有的子進程組成的。
    • waitpid函數還支援其他類型的等待集合,包括UNIX進程組等,不做討論。
  • 修改預設行為(此處書中有問題,作用寫反了)

    可以通過將options設定為常量WHOHANGWUNTRACED的各種組合,修改預設行為。

    • WHOHANG: 如果等待集合中的任何子進程都還沒有終止,那麼立即返回(傳回值為0)
      • 預設的行為返回已終止的子進程。
      • 當你要檢查已終止和被停止的子進程,這個選項會有用。
    • WUNTRACED:掛起調用進程的執行,知道等待集合中的一個進程變為已終結或被停止。
      • 返回的PID為導致的已終止或被停止的子進程PID·
      • 預設的行為是掛起調用進程,直到有子進程終止。
    • WHOHANG|WUNTRACED: 立即返回,如果等待集合中沒有任何子進程被停止或已終止,那麼
  • 檢查已回收子進程的退出狀態

[CSAPP筆記][第八章異常控制流程]

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.