C語言的setjmp:異常處理與構建協作式多任務系統
int setjmp(jmp_buf envbuf)
宏函數setjmp()在緩衝區envbuf中儲存系統堆棧裡的內容,供longjmp()以後使用,setjmp()必須使用標頭檔setjmp.h。
調用setjmp()宏時,傳回值為0,然而longjmp()把一個變原傳遞給setjmp(),該值(恒不為0)就是調用longjmp()後出現的setjmp()的值。
setjmp 函數用於儲存程式的運行時的堆棧環境,接下來的其它地方,你可以通過調用longjmp函數來恢複先前被儲存的程式堆棧環境。當setjmp和 longjmp組合一起使用時,它們能提供一種在程式中實現“非本地局部跳轉”("non-local goto")的機制。並且這種機制常常被用於來實現,把程式的控制流程傳遞到錯誤處理模組之中;或者程式中不採用正常的返回(return)語句,或函數的正常調用等方法,而使程式能被恢複到先前的一個調用常式(也即函數)中。
對setjmp函數的調用時,會儲存程式當前的堆棧環境到env參數中;接下來調用longjmp時,會根據這個曾經儲存的變數來恢複先前的環境,並且當前的程式控制流程,會因此而返回到先前調用setjmp時的程式執行點。此時,在接下來的控制流程的常式中,所能訪問的所有的變數(除寄存器類型的變數以外),包含了longjmp函數調用時,所擁有的變數。
如何?異常處理
首先設定一個跳轉點(setjmp() 函數可以實現這一功能),然後在其後的代碼中任意地方調用 longjmp() 跳回這個跳轉點上,以此來實現當發生異常時,轉到處理異常的程式上,在其後的介紹中將介紹如何?。 setjmp() 為跳轉返回儲存現場並為異常提供處理常式,longjmp() 則進行跳轉(拋出異常),setjmp() 與 longjmp() 可以在函數間進行跳轉,這就像一個全域的 goto 語句,可以跨函數跳轉。
jmp_buf 異常結構
使用 setjmp() 及 longjmp() 函數前,需要先認識一下 jmp_buf 異常結構。jmp_buf 將使用在 setjmp() 函數中,用於儲存當前程式現場(儲存當前需要用到的寄存器的值),jmp_buf 結構在 setjmp.h 檔案內聲明:
typedef struct
&leftsign;
unsigned j_sp; // 堆棧指標寄存器
unsigned j_ss; // 堆棧段
unsigned j_flag; // 標誌寄存器
unsigned j_cs; // 程式碼片段
unsigned j_ip; // 指令指標寄存器
unsigned j_bp; // 基址指標
unsigned j_di; // 目的指標
unsigned j_es; // 附加段
unsigned j_si; // 源變址
unsigned j_ds; // 資料區段
&rightsign; jmp_buf;
jmp_buf 結構存放了程式當前寄存器的值,以確保使用 longjmp() 後可以跳回到該執行點上繼續執行。
setjmp() 與 longjmp() 函數都使用了 jmp_buf 結構作為形參,它們的調用關係是這樣的:
首先調用 setjmp() 函數來初始化 jmp_buf 結構變數 jmpb,將當前CPU中的大部分影響到程式執行的積存器存入 jmpb,為 longjmp() 函數提供跳轉,setjmp() 函數是一個有趣的函數,它能返回兩次,它應該是所有庫函數中唯一一個能返回兩次的函數,第一次是初始化時,返回零,第二次遇到 longjmp() 函數調用後,longjmp() 函數使 setjmp() 函數發生第二次返回,傳回值由 longjmp()
的第二個參數給出(整型,這時不應該再返回零)。
在使用 setjmp() 初始化 jmpb 後,可以其後的程式中任意地方使用 longjmp() 函數跳轉會 setjmp() 函數的位置,longjmp() 的第一個參數便是 setjmp() 初始化的 jmpb,若想跳回剛才設定的 setjmp() 處,則 longjmp() 函數的第一個參數是 setjmp() 所初始化的 jmpb 這個異常,這也說明一件事,即 jmpb 這個異常,一般需要定義為全域變數,否則,若是局部變數,當跨函數調用時就幾乎無法使用(除非每次遇到函數調用都將
jmpb 以參數傳遞,然而明顯地,是不值得這樣做的);longjmp() 函數的第二個參數是傳給 setjmp() 的第二次傳回值