目前,我遇到了用戶抱怨我的應用程式終止的問題。在某些(看似任意)條件和桌面環境下,應用程式不會終止,且重新啟動時不會儲存設定。我在相關的 irc 頻道中詢問,大多數時候我被告知要正確處理訊號。我知道終端機中的 Ctrl-C 的 SIGINT 和「正常」終止的 SIGTERM 。但有人告訴我 SIGHUP 也很重要。所以我的問題是:
我必須處理哪些訊號才能建立行為良好的應用程式?
答案1
https://en.wikipedia.org/wiki/Unix_signal
有一個標準訊號及其預設操作的清單。任何有足夠特權的進程總是可以向您發送任何訊號,但進程(或您,透過進程)不應該這樣做。
您應該只kill
使用並期望您的流程kill
使用學期以及一堆可以由您的環境(shell、終端驅動程式)產生的訊號,除非您知道目標處理您想要發送的特殊訊號。
您可以按操作對基本訊號進行排序。
核心轉儲訊號
ABRT TRAP SYS BUS FPE ILL SEGV XFSZ XCPU QUIT
要么是由您(TRAP、ABRT、SYS)使用某些專用函數產生的,要么是由您進入硬錯誤狀態(BUS、FPE、ILL、SEGV)產生的。
QUIT
由使用者在想要核心轉儲的終端上產生 ( C+\
)。
您可能希望將它們保留為預設配置。
出於終止訊號:
HUP INT PIPE TERM ALRM POLL PROF USR1 USR2
你應該期待
HUP INT PIPE TERM
根據您的環境在不同情況下的情況:
HUP -- when the terminal hungs up or you become a stopped orphaned process
INT -- when the user at the terminal interrupts you with C-c
PIPE -- when a PIPE or socket you write into closes, e.g. by
exiting before you finished writing to it
(`yourprogram | (exit)` will give you a `PIPE`
if yourprogram attempts to write to its STDOUT)
TERM -- when a process ends you a normal termination request
只有當您自己設定時,您才應該收到其餘的終止訊號。
殺和停止你無能為力。
您可能希望攔截終端產生的停止訊號:
TTIN -- you read from the terminal and you're a backgrounded process
TTOU -- you write the terminal when you're a backgrounded process
and the terminal is set to put you to sleep when you do
TSTP -- you're being put to sleep with `C-Z`
但最糟的情況是,這些只會停止您的進程,而不會破壞您的狀態。
除非您知道需要處理CHLD
、CONT
、 或URG
,否則您不需要。
長話短說:
基本上,我認為如果您通常想要進行一些退出前清理,則應該處理(或忽略)HUP、INT、PIPE 和 TERM。其餘的可以不用管,除非你的程式使用這些訊號,或者除非你在所有情況下都絕對需要一些清理。
在後一種情況下,您可以空白地阻止所有未處理的訊號,但請注意訊號阻止遮罩會在fork 和execve 呼叫之間繼承,並忽略或阻止像ILL 這樣的訊號(如果它們是在進程運行時產生的並且沒有發送給您)透過kill 或sigqueue 會給你未定義的行為。
如果您想了解更多信息,請瀏覽線上說明頁面和標準。訊號在 Unix 上是一個相當大的話題,處理它們可能會變得非常棘手。
答案2
如果您這樣做,您可能最終會忽略大量訊號,僅僅因為其中一個訊號正在終止您的程式。正如 sblingx 在評論中所建議的,最好的做法是分析這些訊號並了解它們來自哪裡。畢竟,你的程式被殺死可能是有充分理由的。
話雖如此,你可以看看signals(7)
取得可能導致進程終止的訊號清單:
SIGHUP、SIGINT、SIGQUIT、SIGILL、SIGABRT、SIGFPE、SIGKILL、SIGSEGV、SIGPIPE、SIGALRM、SIGTERM、SIGUSR1 和 SIGUSR2。
如果您的應用程式花費一些時間連接到終端,那麼處理SIGINT
是一個好主意(畢竟Ctrl+非常有名)。也是一個好主意,因為它仍然是基本的終止訊號,並且由核心使用(例如,在系統關閉時)。可能是前兩個版本的“更嚴厲”版本,因為它會導致核心轉儲。當您收到該訊息時,您的程式可能會立即崩潰。在收到任何其他資訊後,您的程式將被期望“清理混亂並離開”(保存設置,正確終止)。CSIGTERM
SIGQUIT
SIGILL
、SIGFPE
、SIGSEGV
和SIGPIPE
是與程序不當行為相關的訊號。分段故障可能是最常見的情況,但最終,所有這些情況都可以被視為錯誤,我不認為捕獲訊號是從這些情況中恢復的最佳方法。
- 大多數時候,編譯器不會
SIGILL
輕易讓這種情況發生(更多這裡)所以我想你可以把這個放在一邊。 - 確保你永遠不會除以零,這樣你就可以避免大多數
SIGFPE
s。除此之外,只需確保您的程式擅長數學並且不會對數字感到瘋狂。 SIGSEGV
可能是由多種原因引起的(非法記憶體存取、非法取消引用),對於這一點,我建議在程式執行時使用它來監視程序,並使用堆疊gdb
追蹤討厭的指令。gdb
- 最後,
SIGPIPE
主要與資料流相關(讀取檔案、連接…)。如果您的任何程式 I/O 流在執行時可能會終止,您應該確保您的程式在使用它們之前檢查其描述符,並處理其上發生的所有 I/O 事件(例如,在使用select
或時poll
) 。gdb
在這裡可能也有用。
SIGABRT
、SIGALRM
、SIGUSR1
和SIGUSR2
基本上是使用者訊號。如果你收到了它們,很可能你真的做到了。
這給我們留下了SIGHUP
和SIGKILL
,它可能不會被捕獲。
SIGHUP
例如,如果您從終端啟動進程,然後關閉該終端而不分離進程(disown
或使用nohup
),就會發生這種情況。如果您的程式從終端啟動並作為守護程序運行,請不要忘記使其與 shell 分離,例如透過雙重分叉。如果您的程式可以從 GUI 啟動,或者在啟動時控制終端不太可能“關閉”,則此特定訊號的相關性較小。SIGKILL
如你所知,這是萬能的信號。這是核心說「滾蛋!」的方式。如果您收到了該訊號,那麼您應該將注意力集中在發出訊號的任何內容上,而不是如何在程式中處理它。
最後,分析訊號並使用偵錯工具可能是解決這些問題的最簡單方法。如果當你的程式出現問題時內核觸發了一個訊號,gdb
應該可以幫助你定位問題。在這種情況下,請忘記該訊號,而專注於它告訴您的錯誤。
如果訊號來自“外部”,找到發射器可能是最好的做法(如果實在找不到的話追蹤這kill(2)
系統呼叫可能是最後的選擇)。然後您可以決定是否要忽略它 ( SIG_IGN
),或考慮它。沉默發射程序也可以是不錯的選擇...