異常控制流

2021-08-02 16:30:43 字數 4468 閱讀 3406

從給處理器加電開始,直到斷電為止,程式計數器假設乙個值的序列a\0,a\1,…a\n-1,其中,每個a\k是某個相應的指令i\k的位址。每次從a\k到a(k+1)的過渡稱為控制轉移。這樣的控制轉移序列叫做處理器的控制流

現代系統通過使控制流發生突變來對這些情況作出反應,這些突變稱為異常控制流(ecf)。異常控制流發生在計算機系統的各個層次。比如,在硬體層,硬體檢測到的事件會觸發控制突然轉移到異常處理程式。在作業系統層,核心通過上下文切換將控制從乙個使用者程序轉移到另乙個程序。在應用層,乙個程序可以傳送訊號到另乙個程序,而接收者會將控制突然轉移到它的乙個訊號處理程式。

異常:是異常控制流的一種形式,它一部分是由硬體實現的,一部分是由作業系統實現的。

異常就是控制流的突變,用來響應處理器狀態中的某些變化。

系統中可能的每種型別的異常都分配了乙個唯一的非負整數的異常號。在系統啟動時,作業系統分配和初始化一張稱為異常表的跳轉表,使得條目k包含異常k的處理程式的位址。異常號是到異常表中的索引

異常的型別:

陷阱是有意的異常,是執行一條指令的結果。陷阱最重要的用途是在使用者程式和核心之間提供乙個像過程一樣的介面,叫做系統呼叫。系統呼叫執行在核心模式中,核心模式允許系統呼叫執行指令,並訪問定義在核心中的棧。

linux提供上百種系統呼叫,當應用程式想要請求核心服務時可以使用,包括讀檔案、寫檔案或是建立乙個新程序

程序的經典定義就是乙個執行中的程式的例項。系統中的每個程式都是執行在某個程序的上下文中的。上下文是由程式正確執行所需的狀態組成的。

我們用單步偵錯程式單步執行程式,我們會看到一系列的程式計數器(pc)的值,這些值唯一地對應於包含在程式的可執行目標檔案中的指令,或者是包含在執行時動態鏈結到程式的共享物件中的指令。pc值的序列叫做邏輯控制流

位址空間組織結構:位址空間底部是保留給使用者程式的,包含通用的文字、資料、堆和棧段。位址空間頂部是保留給核心的。

處理器通常用某個控制暫存器中的乙個模式位提供一種機制,限制乙個應用可以執行的指令以及它可以訪問的位址空間範圍。當設定了模式位,程序就執行在核心模式中(或超級使用者模式)。乙個執行在核心模式的程序可以執行指令集中的任何指令,並且可以訪問系統中任何儲存器位置

作業系統核心使用一種稱為上下文切換的異常控制流來實現多工。核心為每個程序維持乙個上下文。

上下文切換

系統呼叫錯誤處理

當unix系統級函式遇到錯誤時,它們典型地會返回-1,並設定全域性整數變數errno來表示什麼出錯了

程序控制

exit函式以status退出狀態來終止程序

父程序通過呼叫fork函式建立乙個新的執行子程序

#include #include pid_t fork(void);

返回:子程序返回0,父程序返回子程序的pid,如果出錯,則為-1

父程序與新建立的子程序之間最大的區別在於它們有不同的pid

fork函式只被呼叫一次,卻會返回兩次;一次是在呼叫程序(父程序)中,fork返回子程序的pid,在子程序中,fork返回0,子程序的pid總是非零的

int main()

printf("parent:x=%d\n",--x);

exit(0);}

這個簡單的例子有一些微妙的方面

如果父程序沒有**它的殭屍子程序就終止了,那麼核心就會安排init程序來**它們。init程序的pid為1,並且是在系統初始化時由核心建立的。長時間執行的程式,總是應該**它們的殭屍子程序。即使殭屍子程序沒有執行,它們仍然消耗系統的儲存器資源

乙個程序可以通過呼叫waitpid函式來等待它的子程序終止或者停止

#include #include pid_t waitpid(pid_t pid,int* status,int options);

返回:如果成功,則為子程序的pid,如果wnohang,則為0,如果其他錯誤,則為-1

預設地(當options=0時),waitpid掛起呼叫程序的執行,直到它的等待集合中的乙個子程序終止。如果等待集合中的乙個程序在剛呼叫的時候就已經終止了,那麼waitpid就立即返回,在這兩種情況下,waitpid返回導致waitpid返回的已終止子程序的pid,並且將這個已終止的子程序從系統中去除。

如果呼叫程序沒有子程序,那麼waitpid返回-1,並且設定errno為echild。如果waitpid函式被乙個訊號中斷,那麼它返回-1,並設定errno為eintr

讓程序休眠

sleep函式將乙個程序掛起一段指定的時間

#include unsigned int sleep(unsigned int secs);

int pause(void);

pause函式讓呼叫函式休眠,直到該程序收到乙個訊號

載入並執行程式

execve函式在當前程序的上下文中載入並執行乙個新程式

#include int execve(const char* filename,const char* argv,const char* envp);

如果成功,則不返回,如果錯誤,則返回-1

execve函式載入並執行可執行目標檔案filename,且帶引數列表argv和環境變數列表envp。只有當出現錯誤時,例如找不到filename,execve才會返回到呼叫程式。所以,與fork一次呼叫返回兩次不同,execve呼叫一次並從不返回。

fork函式在新的子程序執行相同的程式,新的子程序是父程序的乙個複製品。execve函式在當前程序的上下文中載入並執行乙個新的程式。它會覆蓋當前程序的位址空間,但沒有建立乙個新程序,新的程式仍然有相同的pid,並且繼承了呼叫execve函式時已開啟的所有檔案描述符

訊號:它允許程序中斷其他程序

乙個訊號就是一條小訊息,它通知程序系統中發生了乙個某種型別的事件

傳送乙個訊號到目的程序由兩個不同步驟組成:

接收訊號:當目的程序被核心強迫以某種方式對訊號的傳送作出反應時,目的程序就接收了訊號。

傳送訊號:unix系統提供了大量向程序傳送訊號的機制,所有這些機制都是基於程序組這個概念的

預設地,乙個子程序和它的父程序同屬乙個程序組,乙個程序可以使用setpgid函式來改變自己或者其它程序的程序組:

#include int setpgid(pid_t pid,pid_t pgid);
shell使用作業(job)這個抽象概念來表示為對乙個命令列求值而建立的程序。在任何時刻,至多只有乙個前台作業和0個或多個後台作業

接收訊號

訊號處理問題:

可移植的訊號處理:不同系統之間,訊號處理語義的差異(比如乙個被中斷的慢速系統呼叫時重啟還是永久放棄)是unix訊號處理的乙個缺陷。為了處理這個問題,posix(可移植作業系統介面,posix標準定義了作業系統應該為應用程式提供的介面標準)標準定義了sigaction函式,明確的指定他們想要的訊號處理語義

#include int sigaction(int signum,struct sigaction *act,struct sigaction *oldact);

返回:若成功則為0,若出錯則為-1

定義乙個包裝函式signal,它呼叫sigaction。它的呼叫方式與signal函式的呼叫方式一樣。signal包裝函式設定了乙個訊號處理程式,其訊號語義如下:

- 只有這個處理程式當前正在處理的那種型別的訊號被阻塞

- 和所有訊號實現一樣,訊號不會排隊等待

- 只要可能,被中斷的系統呼叫會自動重啟

- 一旦設定了訊號處理程式,它會一直保持,直到signal帶著handler引數為sig_ign或者sig_dfl被呼叫

handler_t *signal(int signum,handler_t *handler)

return (old_action.sa_handler);

}

顯式地阻塞和取消阻塞訊號

- 應用程式可以使用sigprocmask函式顯式地阻塞和取消阻塞選擇的訊號

同步流以避免討厭的併發錯誤

在乙個fork呼叫之後,有些核心排程子程序先執行,而有些核心排程父程序先執行。fork包裝函式,它可以幫助暴露這樣隱藏著的關於父程序和子程序執行順序的假設。其基本思想是每次呼叫fork之後,父程序和子程序扔一枚硬幣決定誰會休眠一會兒,因此給另乙個程序先執行的機會

非本地跳轉

setjmp函式在env緩衝區中儲存當前呼叫環境,以供後面longjmp使用,並返回0.呼叫環境包括程式計數器、棧指標和通用目的暫存器。

longjmp函式從env緩衝區中恢復呼叫環境,然後觸發乙個從最近一次初始化env的setjmp呼叫返回,然後setjmp返回,並帶有非零的返回值retval

setjmp函式只被呼叫一次,但返回多次。一次是當第一次呼叫setjmp,而呼叫環境儲存在緩衝區env中時;一次是為每個相應的longjmp呼叫。longjmp函式被呼叫一次,但從不返回

非本地跳轉的乙個重要應用就是允許從乙個深層巢狀的函式呼叫中立即返回,通常是由檢測到某個錯誤情況引起的。使用非本地跳轉來規避正常的呼叫/返回棧規則

操作程序的工具

top

ps

異常控制流

異常控制流 在作業系統執行程式時,作業系統會把程式具體到每條指令,利用程式指標順序執行指令以達到程式執行的目的。這些指令類似乙個集合,稱之為控制流。程式指標依次執行每條指令,稱之為控制轉移。但是在程式執行過程中因為種種原因,程式狀態發生變化,程式指標不會依次執行這些程式指令,引起程式狀態變化的是存在...

異常控制流

作業系統通過使控制流程發生突變來響應系統狀態變化,這些突變就是異常控制流 exceptional control flow,ecf 異常控制處理流程 1.系統啟動時,作業系統分配和初始化一張異常表,使得索引為k的條目對應異常號為k的處理程式的位址。2.系統執行時,處理器檢測到狀態變化,確定相應的異常...

CSAPP 異常控制流

在一般的情況下,處理器處理的指令序列是相鄰的 順序執行 異常控制流提供了指令的跳轉,它一部分是由硬體實現的,一部分是由作業系統實現的。異常處理 在系統啟動時,作業系統分配和初始化一張稱為異常表的跳轉表 觸發異常時將從跳轉表中找到並執行相應的異常處理程式的 所謂的核心態 系統呼叫 每個系統呼叫都屬於異...