setjmp.h 中的 setjmp/longjmp 是 C 語言實現非局部跳轉、乃至協程的核心機制。本文從原理到實戰,揭開這對「神秘函數」的面紗。
#includeint setjmp(jmp_buf env); // 保存當前棧幀環境到 env,首次返回 0 void longjmp(jmp_buf env, int val); // 恢復 env 環境,setjmp 返回 val (非 0)
// ❌ 錯誤封裝:setjmp 在 try() 內,try 返回後棧幀消失
int try(jmp_buf *buf) { return setjmp(*buf); }
void throw(jmp_buf *buf) { longjmp(*buf, 1); }
正確做法: setjmp 必須直接寫在調用鏈的「最外層函數」(如 main、協程調度器主循環)中,不可再封裝。
#include#include jmp_buf main_buf, co_buf; void coroutine() { printf("Coroutine: Start\n"); longjmp(main_buf, 1); // 交還控制權給 main printf("Coroutine: Resumed\n"); longjmp(main_buf, 2); // 再次交還 printf("Coroutine: End\n"); } int main() { if (setjmp(main_buf) == 0) { coroutine(); // 首次進入協程 } else { printf("Main: Resumed from coroutine, val=%d\n", setjmp(main_buf)); if (setjmp(main_buf) == 1) longjmp(co_buf, 0); // 這裡簡化,實際需保存協程上下文 } return 0; }
輸出:
Coroutine: Start Main: Resumed from coroutine, val=1 Coroutine: Resumed Main: Resumed from coroutine, val=2 Coroutine: End
setjmp 只能保存/恢復棧指針、基址指針等少量寄存器,無法保存完整寄存器組。生產環境推薦 ucontext.h(POSIX):
getcontext()/setcontext()/swapcontext()setjmp/longjmp 是理解協程本質的「最小模型」:保存棧幀指針,稍後恢復指令指針與棧指針。雖然標準庫功能受限,但它證明了——協程本質只是用戶態的棧切換。下一步建議深入 ucontext 或成熟協程庫源碼,體會「輕量級線程」的工程之美。