setjmp, longjmp

Header Files

#include <setjmp.h>

Function Prototype

int setjmp(jmp_buf env);
int longjmp(jmp_buf env, int val);

env

jmp_buf 是 array type,所以當我們將 env 作為參數時,實際上是傳入 env 第一個元素的地址

Function

setjmp

  • setjmp() 會記錄當前程序的執行狀態在 env
  • 直接呼叫 setjmp() 回傳值是 0
  • 否則因為 longjmp() 而回到這行則會回傳 longjmp() 參數中的 val

longjmp

  • longjmp() 會回到 env 當前紀錄的狀態,包括 stack pointer、CPU register、return address 等

如果要在 signal handler 中進行 longjmp,你必須確保 setjmp 已經執行完成

我們的方法是使用 sig_atomic_t 變數在 setjmp 後設為 true 並在 handler 中檢查


Effects on Variables

Effects on Automatic, Register, and Volatile Variables

對於一些 local variable,編譯器可能因為優化而將其放在 register 而非 stack frame 上面,而在 longjmp() 後 register 會回復原樣而 stack frame 上的變數不會

auto

我們沒有指定其放在哪裡,所以他的值在 longjmp() 後是否會改變是未定義的

register

register 變數因為會在 setjmp() 時存在 jmp_buf 中,所以 longjmp() 回來後會回復成 setjmp() 時的狀態

volatile

volatile 會強制將變數放在 stack frame 上而非 register,所以 longjmp() 後並不會回復成 setjmp() 時的值

Effects on Global Variables

因為 global variable 存在 virtual memory 的 initialized data 或 uninitialized data 處,所以不會因為 longjmp() 而回復狀態

Example

Code

#include <setjmp.h>
#include <stdio.h>
 
jmp_buf env;
int global_var = 100;          // 全域變數
static int static_var = 200;   // 靜態變數
 
void deep_function() {
    // 在這裡修改各種變數,然後跳回去
    global_var = 999;
    static_var = 999;
    
    printf("在 deep_function 中,準備 longjmp...\n");
    longjmp(env, 1);  // 跳回 main 中 setjmp 的位置
}
 
int main() {
    // 這些是 main 的區域變數
    int auto_var = 300;                    // 自動變數
    register int reg_var = 400;            // 暫存器變數(提示編譯器)
    volatile int volatile_var = 500;       // volatile 區域變數
    
    int ret = setjmp(env);  // 設定跳躍點
    
    if (ret == 0) {
        // 第一次執行:修改所有變數
        printf("第一次執行 setjmp,返回 0\n");
        printf("修改前:auto_var=%d, reg_var=%d, volatile_var=%d\n", 
               auto_var, reg_var, volatile_var);
        
        // 修改這些變數
        auto_var = 777;
        reg_var = 888;
        volatile_var = 666;
        
        printf("修改後:auto_var=%d, reg_var=%d, volatile_var=%d\n", 
               auto_var, reg_var, volatile_var);
        
        deep_function();  // 這會觸發 longjmp
        
        printf("這行永遠不會執行\n");
    } else {
        // 從 longjmp 跳回來
        printf("\n從 longjmp 回來了!返回值 = %d\n", ret);
        printf("跳回來後:auto_var=%d, reg_var=%d, volatile_var=%d\n", 
               auto_var, reg_var, volatile_var);
        printf("全域變數:global_var=%d, static_var=%d\n", 
               global_var, static_var);
    }
    
    return 0;
}

Output

第一次執行 setjmp,返回 0
修改前:auto_var=300, reg_var=400, volatile_var=500
修改後:auto_var=777, reg_var=888, volatile_var=666
在 deep_function 中,準備 longjmp...

從 longjmp 回來了!返回值 = 1
跳回來後:auto_var=777, reg_var=400, volatile_var=666
全域變數:global_var=999, static_var=999