Unix訊號
在電腦科學中,訊號(英語:Signals)是Unix、類Unix以及其他POSIX相容的作業系統中行程間通訊的一種有限制的方式。它是一種非同步的通知機制,用來提醒程序一個事件已經發生。當一個訊號傳送給一個程序,作業系統中斷了程序正常的控制流程,此時,任何非原子操作都將被中斷。如果程序定義了訊號的處理常式,那麼它將被執行,否則就執行預設的處理常式。
訊號類似於中斷,不同之處在於中斷由處理器調解並由內核處理,而訊號由內核調解(可能透過系統呼叫)並由程序處理。內核可以將中斷作為訊號傳遞給導致中斷的程序(典型的例子有SIGSEGV、SIGBUS、SIGILL和SIGFPE)。
訊號起源於20世紀70年代的貝爾實驗室Unix,最近在POSIX標準中有所規定。
嵌入式程序可能會發現訊號對於行程間通訊很有用,因為訊號的計算和主記憶體佔用很小。
傳送訊號
- 在一個執行的程序的控制終端鍵入特定的組合鍵可以向它傳送某些訊號:
- kill()系統呼叫會在權限允許的情況下向程序傳送特定的訊號,類似地,kill命令允許用戶向程序傳送訊號。
raise(3)
庫函數可以將特定訊號傳送給目前程序。 - 像除數為零、段錯誤這些異常也會產生訊號(這裏分別是SIGFPE和SIGSEGV,預設都會導致程序終止和核心傾印)。
- 內核可以向程序傳送訊號以告知它一個事件發生了。例如當程序將數據寫入一個已經被關閉的管道時將會收到SIGPIPE訊號,預設情況下會使程序關閉。
處理訊號
訊號處理常式可以透過signal()
系統呼叫來設置。如果沒有為一個訊號設置對應的處理常式,就會使用預設的處理常式,否則訊號就被程序截獲並呼叫相應的處理常式。在沒有處理常式的情況下,程序可以指定兩種行為:忽略這個訊號(SIG_IGN)或者用預設的處理常式(SIG_DFL)。但是有兩個訊號是無法被截獲並處理的:SIGKILL和SIGSTOP。
風險
因為競態條件的存在,訊號的處理是有弱點的。因為訊號是非同步的,所以在處理一個訊號的過程中,程序可能收到另一個訊號(甚至是相同的訊號)。sigprocmask()
系統呼叫可以用來阻塞和恢復訊號的傳遞。訊號可以造成程序中系統呼叫的中斷,並在訊號處理完後重新開始未完成的系統呼叫。訊號處理常式應該沒有任何不想要的副作用,比如,errno的改變、訊號遮罩的改變、訊號處理方法的改變,以及其他全域程序性質的改變。在訊號處理常式內使用不可重入函數,如malloc和printf,也是不安全的。
與硬件異常的關係
程序的執行也可能導致硬件異常,例如,將一個數除以零,或者出現TLB不命中。在類Unix系統中,這會自動執行內核的例外處理程序。對於某些異常如頁缺失,內核有足夠的資訊來處理完並恢復程序的執行。但是對於另外一些異常,內核不能處理而只能透過傳送訊號把異常交給程序自己處理。例如在x86架構的CPU上,如果一個程序嘗試將一個數除以零,將會產生divide error異常,並使內核向出錯的程序傳送SIGFPE訊號。相似地,如果一個程序嘗試訪問虛擬地址空間以外的主記憶體,內核將向程序傳送SIGSEGV訊號。異常與訊號的具體對應關係在不同的CPU架構上是不同的。
訊號列表
單一UNIX規範規定了在<signal.h>中定義的訊號有:
備註:打星號的部分表示這是X/Open System Interfaces (XSI)擴充的部分。使用引號的文字是參照自SUS[1] (頁面存檔備份,存於互聯網檔案館)。
- SIGABRT 和 SIGIOT
SIGABRT 和 SIGIOT 訊號能讓程序異常終止(abort)。 該訊號通常是由程序自身呼叫 C標準函式庫 的 abort()
函數來觸發, 但它也可以像其它訊號一樣由外部傳送給程序。
- SIGALRM, SIGVTALRM 和 SIGPROF
如果你用 setitimer 這一類的報警設置函數設置了一個時限,到達時限時程序會接收到 SIGALRM, SIGVTALRM 或者 SIGPROF。但是這三個訊號量的含義各有不同,SIGALRM 計時的是真即時間,SIGVTALRM計時的是程序使用了多少CPU時間,而 SIGPROF 計時的是程序和代表該程序的內核用了多少時間。
- SIGBUS
匯流排發生錯誤時,程序接收到一個SIGBUS訊號。舉例來說,記憶體訪問對齊或者或不存在對應的實體位址都會產生SIGBUS訊號。
- SIGCHLD
當子程序終止、被中斷或被中斷後恢復時,SIGCHLD訊號被傳送到程序。該訊號的一個常見用法是指示作業系統在子程序終止後清理其使用的資源,而不顯式呼叫等待系統呼叫。
- SIGCONT
SIGCONT訊號指示作業系統繼續(重新啟動)先前由SIGSTOP或SIGTSTP訊號暫停的程序。Unix 殼的作業控制是該訊號的一個重要應用。
- SIGFPE
當程序執行了一個錯誤的算術運算時,例如被零除,訊號被傳送到一個程序。這可能包括整數被零除,以及整數在除結果中溢位(在C中只有INT_MIN/-1、INT64_MIN/-1和%-1會觸發該行為)。注意該訊號與浮點數溢位無關。
- SIGHUP
檢測到控制中斷掛起或者控制程序死亡時,程序會收到 SIGHUP。現在作業系統,該訊號通常意味着使用的 虛擬終端 已經被關閉。許多 守護行程 在接收到該訊號時,會多載他們的設置和重新打開紀錄檔檔案(logfiles),而不是去結束程式。nohup 命令用於無視該訊號。
- SIGILL
當程序試圖執行非法、格式錯誤、未知或特權指令時,SIGILL訊號被傳送到該程序。
- SIGINT
當用戶希望中斷程序時,SIGINT訊號由用戶的控制終端傳送到程序。這通常透過按下Ctrl+C來傳送,但是在某些系統中,可以使用「DELETE」鍵或「BREAK」鍵。
- SIGKILL
傳送SIGKILL訊號到一個程序可以使其立即終止(KILL)。與SIGTERM和SIGINT相不同的是,這個訊號不能被擷取或忽略,接收過程在接收到這個訊號時不能執行任何清理。以下例外情況適用:
- 殭屍程序不能被殺死,因為它們已經死了,正在等待它們的父程序來收穫它們。
- 處於阻塞狀態的程序不會死亡,直到它們再次醒來。
- init 程序是特殊的: init不接收任何它不打算處理的訊號,因此它會忽略SIGKILL。[1]這條規則有一個例外,Linux 上的 init 如果被 ptrace 了,那麼它是可以接收 SIGKILL 並被殺死的[2][3]。
- 處於不可中斷的睡眠的程序即使傳送了SIGKILL,也有可能不會終止(並釋放其資源)。這是少數 Unix 系統必須重新啟動才能解決臨時軟件問題的幾種情況之一。
當在大多數系統關閉程序中終止程序時,如果程序沒有響應 SIGTERM 而自動退出,SIGKILL 是最後的手段。為了加快電腦關機過程,Mac OS X 10.6向標記自己為「clean」的程序傳送SIGKILL,從而加快關機時間,而且可能不會產生任何不良影響。[4]。在 Linux 中執行 killall -9
命令具有類似不過更危險的效果;它不讓程序儲存未儲存的數據。
- SIGPIPE
當一個程序試圖寫入一個沒有連接到另一端程序的管道時,SIGPIPE訊號會被傳送到該程序。
- SIGPOLL
當一個事件發生在一個正在顯式監視的檔案描述子上時,就會傳送SIGPOLL訊號。有效使用這種用法可以進行非同步 I/O,因為內核將代替呼叫者輪詢描述符。它提供了主動輪詢的替代方案。
- SIGRTMIN 到 SIGRTMAX
SIGRTMIN至SIGRTMAX訊號用於用戶自訂的目的。它們是即時訊號。
- SIGTTIN 和 SIGTTOU
當程序在後台試圖分別從tty讀取或寫入時,SIGTTIN和SIGTTOU訊號會被傳送到該程序。通常,這些訊號僅由作業控制下的程序接收;守護行程沒有控制終端,因此永遠不會接收這些訊號。
- SIGQUIT
當用戶在程序的控制終端請求退出程序並進行核心傾印時,SIGQUIT訊號會被傳送到該程序。
- SIGSEGV
當程序試圖訪問無效主記憶體參照時發生記憶體區段錯誤,SIGSEGV訊號會被傳送到該程序。
- SIGSTOP
當作業系統暫停程序的執行時,會產生SIGSTOP訊號。SIGSTOP訊號無法被擷取或無視。
- SIGSYS
當系統呼叫時傳入非法的參數,會產生SIGSYS訊號。實際上,SIGSYS訊號很少會出現,因為應用程式依賴庫呼叫系統呼叫。SIGSYS可以被違反Linux Seccomp安全規則的應用程式擷取。SIGSYS也可用於模擬外部系統呼叫,例如:在Linux上模擬 Windows系統呼叫。
- SIGTERM
當用戶請求終止程序時,會產生SIGTERM訊號。SIGTERM訊號可以被擷取或無視。這允許該程序在結束前釋放掉所佔用的資源並儲存其狀態。SIGINT和SIGTERM非常相似。
- SIGTSTP
當用戶在程序的控制終端請求退出程序時,會產生SIGTSTP訊號。SIGTSTP訊號可以被擷取或無視。SIGTSTP訊號的產生通常是由於用戶按下Ctrl+Z。
預設行為
一個程序可以自訂如何處理傳入的POSIX訊號。如果一個程序沒有定義一個訊號處理程序,那麼這個訊號的預設處理程序將被使用。下表列出了一些與POSIX相容的UNIX系統的預設操作,例如FreeBSD、OpenBSD和Linux。
訊號 | 可移植代號 | 預設行為 | 描述 |
---|---|---|---|
SIGABRT | 6 | 終止 (核心傾印) | 程序終止訊號 |
SIGALRM | 14 | 終止 | 計時器告警 |
SIGBUS | 不適用 | 終止 (核心傾印) | 訪問主記憶體對象未定義區域 |
SIGCHLD | 不適用 | 忽略 | 子程序終止、暫停、繼續 |
SIGCONT | 不適用 | 繼續 | 如果被暫停,重新繼續執行 |
SIGFPE | 8 | 終止 (核心傾印) | 錯誤的算術運算 |
SIGHUP | 1 | 終止 | 掛起 |
SIGILL | 4 | 終止 (核心傾印) | 非法的指令 |
SIGINT | 2 | 終止 | 終端中斷訊號 |
SIGKILL | 9 | 終止 | 殺死 (無法被擷取或忽略的訊號) |
SIGPIPE | 13 | 終止 | 寫入一個沒有連接另一端的管道 |
SIGPOLL | 不適用 | 終止 | 可輪詢事件 |
SIGPROF | 不適用 | 終止 | 效能調優定時器逾時 |
SIGQUIT | 3 | 終止 (核心傾印) | 終端退出訊號 |
SIGSEGV | 11 | 終止 (核心傾印) | 非法的主記憶體參照 |
SIGSTOP | 不適用 | 暫停 | 暫停執行(無法被擷取或忽略的訊號) |
SIGSYS | 不適用 | 終止 (核心傾印) | 錯誤的系統呼叫 |
SIGTERM | 15 | 終止 | 終止訊號 |
SIGTRAP | 5 | 終止 (核心傾印) | 追蹤/斷點陷阱 |
SIGTSTP | 不適用 | 暫停 | 終端中止訊號 |
SIGTTIN | 不適用 | 暫停 | 後台程序嘗試讀 |
SIGTTOU | 不適用 | 暫停 | 後台程序嘗試寫 |
SIGUSR1 | 不適用 | 終止 | 用戶自訂訊號1 |
SIGUSR2 | 不適用 | 終止 | 用戶自訂訊號2 |
SIGURG | 不適用 | 忽略 | Out-of-band data is available at a socket |
SIGVTALRM | 不適用 | 終止 | 虛擬定時器逾時 |
SIGXCPU | 不適用 | 終止 (核心傾印) | 超出CPU時間限制 |
SIGXFSZ | 不適用 | 終止 (核心傾印) | 超出檔案大小限制 |
SIGWINCH | 不適用 | 忽略 | 終端窗口大小已變化 |
- 可移植編號
對於大多數訊號,相應的訊號編號由實現定義。此列列出了POSIX標準中指定的數字。
- 行為釋義
- 終止 – 程序異常終止。程序終止的結果和呼叫 _exit() 是一樣的,除了終止可以向 wait() 和 waitpid() 返回導致程序終止的訊號。
- 終止(核心傾印) – 程序異常終止。這種程序中止的過程根據實現有所不同,一般會建立一個核心檔案。
- 忽略 – 程序忽略該訊號。
- 暫停 – 程序被暫停(不是終止)。
- 繼續 – 程序恢復執行。
參考文獻
- ^ https://manpages.ubuntu.com/manpages/zesty/man2/kill.2.html (頁面存檔備份,存於互聯網檔案館) section NOTES
- ^ SIGKILL init process (PID 1). Stack Overflow.
- ^ Can root kill init process?. Unix & Linux Stack Exchange.
- ^ Mac Dev Center: What's New in Mac OS X: Mac OS X v10.6. 2009-08-28 [18 November 2017]. (原始內容存檔於2010-01-14).
外部連結
- Introduction to Unix Signals Programming
- Another Introduction to Unix Signals Programming (頁面存檔備份,存於互聯網檔案館)
- UNIX and Reliable POSIX Signals (頁面存檔備份,存於互聯網檔案館) by Baris Simsek
- Signal Handlers (頁面存檔備份,存於互聯網檔案館) by Henning Brauer
參見
<signal.h>