行程間通訊

信號處理常式與信號處置(signal handler and disposition)

信號到達時,行程必須對它做點什麼——但做什麼?這個決定就是信號的處置方式(disposition),每個信號剛好有三種選擇。預設動作:讓核心做這個信號平常做的事(通常是終止)。忽略它:把它丟掉,彷彿從未到來。或者攔截它:在信號落地的那一刻,執行一個你自己寫的小函式,稱為信號處理常式。處置方式是你事先選定的逐行程設定,每個信號一個。

你用 signal() 設定它,或更穩健地用 sigaction()。舉例來說,要讓 Ctrl-C 執行清理而不是把你殺掉,你為 SIGINT 安裝一個處理常式:sigaction(SIGINT, &act, NULL),其中 act.sa_handler 指向你的函式。從此以後,當 SIGINT 到達,核心會暫停你的正常程式碼、跳進你的處理常式、執行它,再回到剛剛被打斷的那個位置。要忽略一個信號,把它的處置設成 SIG_IGN;要恢復正常行為,設成 SIG_DFL。常見模式是:攔截 SIGTERM 以把緩衝區寫出、關閉檔案,再 exit——這樣程式在被要求時能乾淨地關閉,而非丟失資料。

陷阱——而且是嚴重的一個——在於處理常式是在借來的一瞬間執行的,它打斷了你程式當下正在做的事,可能正卡在 malloc() 或 printf() 中間。所以處理常式只能安全地呼叫一小串「async-signal-safe(非同步信號安全)」的函式,而最乾淨的模式是讓處理常式幾乎什麼都不做:設一個 volatile sig_atomic_t 旗標就返回,留給你的主迴圈去注意這個旗標、稍後再做真正的工作。有兩個處置永遠無法改變:SIGKILL 與 SIGSTOP 永遠採取它們的預設動作,所以你寫的任何處理常式都永遠不會為它們執行。

volatile sig_atomic_t stop = 0; void on_term(int sig) { stop = 1; } /* 極小、安全的處理常式 */ /* 設定時: */ struct sigaction a = {0}; a.sa_handler = on_term; sigaction(SIGTERM, &a, NULL); /* 主迴圈檢查: */ while (!stop) { do_work(); }

最佳實務:處理常式只設一個旗標;主迴圈再安全地做真正的關閉工作。

優先用 sigaction() 而非較舊的 signal(),後者的行為在不同系統間歷來並不一致。也絕不要假設處理常式會不被打斷地跑完——它執行時可能又有另一個信號到達。

又称
signal dispositiondefault/ignore/catch信號處理常式信號處置