Linux 應用程式開發和訊號處理

Linux 應用程式開發和訊號處理

目前,我遇到了用戶抱怨我的應用程式終止的問題。在某些(看似任意)條件和桌面環境下,應用程式不會終止,且重新啟動時不會儲存設定。我在相關的 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`

但最糟的情況是,這些只會停止您的進程,而不會破壞您的狀態。

除非您知道需要處理CHLDCONT、 或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+非常有名)。也是一個好主意,因為它仍然是基本的終止訊號,並且由核心使用(例如,在系統關閉時)。可能是前兩個版本的“更嚴厲”版本,因為它會導致核心轉儲。當您收到該訊息時,您的程式可能會立即崩潰。在收到任何其他資訊後,您的程式將被期望“清理混亂並離開”(保存設置,正確終止)。CSIGTERMSIGQUIT

SIGILLSIGFPESIGSEGVSIGPIPE是與程序不當行為相關的訊號。分段故障可能是最常見的情況,但最終,所有這些情況都可以被視為錯誤,我不認為捕獲訊號是從這些情況中恢復的最佳方法。

  • 大多數時候,編譯器不會SIGILL輕易讓這種情況發生(更多這裡)所以我想你可以把這個放在一邊。
  • 確保你永遠不會除以零,這樣你就可以避免大多數SIGFPEs。除此之外,只需確保您的程式擅長數學並且不會對數字感到瘋狂。
  • SIGSEGV可能是由多種原因引起的(非法記憶體存取、非法取消引用),對於這一點,我建議在程式執行時使用它來監視程序,並使用堆疊gdb追蹤討厭的指令。gdb
  • 最後,SIGPIPE主要與資料流相關(讀取檔案、連接…)。如果您的任何程式 I/O 流在執行時可能會終止,您應該確保您的程式在使用它們之前檢查其描述符,並處理其上發生的所有 I/O 事件(例如,在使用select或時poll) 。gdb在這裡可能也有用。

SIGABRTSIGALRMSIGUSR1SIGUSR2基本上是使用者訊號。如果你收到了它們,很可能你真的做到了。

這給我們留下了SIGHUPSIGKILL,它可能不會被捕獲。

  • SIGHUP例如,如果您從終端啟動進程,然後關閉該終端而不分離進程(disown或使用nohup),就會發生這種情況。如果您的程式從終端啟動並作為守護程序運行,請不要忘記使其與 shell 分離,例如透過雙重分叉。如果您的程式可以從 GUI 啟動,或者在啟動時控制終端不太可能“關閉”,則此特定訊號的相關性較小。

  • SIGKILL如你所知,這是萬能的信號。這是核心說「滾蛋!」的方式。如果您收到了該訊號,那麼您應該將注意力集中在發出訊號的任何內容上,而不是如何在程式中處理它。

最後,分析訊號並使用偵錯工具可能是解決這些問題的最簡單方法。如果當你的程式出現問題時內核觸發了一個訊號,gdb應該可以幫助你定位問題。在這種情況下,請忘記該訊號,而專注於它告訴您的錯誤。

如果訊號來自“外部”,找到發射器可能是最好的做法(如果實在找不到的話追蹤kill(2)系統呼叫可能是最後的選擇)。然後您可以決定是否要忽略它 ( SIG_IGN),或考慮它。沉默發射程序也可以是不錯的選擇...

相關內容