天天看點

了解Linux信号

文章目錄

          • 什麼是信号
          • 信号的類型
          • 發送信号
          • 捕獲信号
            • 清理臨時檔案
            • 忽略信号
            • 重置traps
          • 注意事項
          • 參考文檔

本文根據參考文檔翻譯整理而來,概述了常見的信号以及如何在bash腳本中處理信号。

什麼是信号

    信号是系統響應特定條件而産生的事件。

    當系統檢測到軟體事件時,可以向程序發送信号。信号可以由使用者生成,也可以由程序生成。在發生硬體事件(比如硬體故障)或完成I/O事件時,核心也可以發送信号。

    信号與中斷類似,差別在于中斷由硬體産生,處理器中轉并由核心處理,而信号由核心(也可能通過系統調用)中轉并由程序處理。核心可以将中斷作為信号傳遞給引發中斷的程序,例如SIGSEGV,SIGBUS, SIGILL 和 SIGFPE。

    當一個程序收到一個信号時,它可以采取一個動作。标準信号有一個預設的配置,它決定了程序在接收信号後的行為。标準信号的預設配置可以是man手冊中指定的以下選項之一:

  • Term 預設操作是終止程序。
  • Ign 預設操作是忽略信号。
  • Core 預設操作是終止程序并轉儲核心。這将建立一個名為core的檔案,其中包含程序在接收到信号時的記憶體映像。
  • Stop 預設操作是停止程序。
  • Cont 預設操作是在程序目前停止時繼續程序。

    當一個程序收到一個信号時,它可以選擇執行這些操作之一:

  • 執行信号的預設配置
  • 通過設定信号屏蔽來阻塞信号
  • 使用信号系統調用将預設配置替換為自定義操作
信号的類型

    每一個信号都屬于五類之一:

  • 程序控制
  • 資源控制
  • I/O通知
  • 硬體狀态
  • 軟體狀态

    Linux支援很多信号。你可以在官方的手冊頁(man 7 signal)上找到很多關于它的資訊。要檢視系統上所有可用信号的清單,請運作 kill -l 指令:

# kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX
           

    以下是常見的信号:

    SIGHUP (1):如果某個程序正在從某個終端運作,而該終端意外關閉,則該程序接收到該信号。“HUP”是“hang up”的縮寫并終止程序。如果不想在終端斷開連接配接時挂斷程序,請使用nohup啟動程序。

    SIGINT (2):從鍵盤手動中斷程序。當您點選Ctrl-C時,它會向正在運作的程序發送一個SIGINT信号并終止它。

    SIGQUIT(3):當使用者在終端中鍵入 <ctrl>+\ 時,它将強制程序生成核心轉儲并終止程序。

    SIGKILL(9):強制終止程序。該信号不能被阻止或處理。當程序被SIGKILL信号終止,該程序不能執行清理。

    SIGPIPE (13):損壞的管道。如果一個程序在其輸出中通過管道傳輸到另一個程序(producer | consumer),并且如果consumer死亡,那麼producer将從consumer獲得SIGPIPE信号,進而終止producer程序。

    SIGTERM (15):軟體終止信号。這是kill指令預設發送的信号。

    SIGCHLD(17):子程序将此信号發送給其父程序,告知子程序已停止或終止。如果子程序在父程序調用 wait 系統調用之前終止,核心将嘗試保留有關該程序的資訊,以便父程序稍後可以調用wait來了解退出狀态。子程序的這種狀态,即程序終止但子程序的條目仍然存在于核心的程序表中,稱為僵屍狀态,該程序稱為僵屍程序。

    SIGUSR1, SIGUSR2:為開發者保留的信号。

發送信号

    除了像 <ctrl>+\ 和 <ctrl>+c 這樣的鍵盤信号之外,kill 指令還可以通過使用程序的PID向程序發送特定的信号。

# sends a SIGINT
kill -2 <pid> <pid>

# sends a SIGKILL
kill -9 <pid>

#sends a SIGQUIT
kill --signal SIGQUIT <pid>
           

    如果想删除同一類型的所有程序,那麼killall就很友善了:

# kills all ssh processes
kill-all ssh
           
捕獲信号

    當程序接收到特定信号時,用來做某事的指令是trap指令。在shell程式執行期間,當您在終端上按Ctrl+C或Break鍵時,通常該程式會立即終止,并傳回指令提示符。有時這樣并不好,例如,可能會留下一堆無法清理的臨時檔案。

    Shell腳本中的trap指令有兩種常見用途:

  • 清理臨時檔案
  • 忽略信号

    trap指令有以下文法:

$ trap commands signals
           

    這裡的command可以是任何有效的Unix指令,甚至是使用者定義的函數,signal可以是要捕獲的任意數量的信号的清單。

清理臨時檔案

    作為trap指令的一個示例,下面顯示了如果試圖從終端中斷程式,如何删除一些檔案,然後退出:

$ trap "rm -f $WORKDIR/work1$$ $WORKDIR/dataout$$; exit" 2
           

    從shell程式中執行此trap的點開始,如果程式接收到2号信号,則 work1$$ 和 dataout$$ 兩個檔案将自動删除。

是以,如果使用者在執行此trap後中斷程式的執行,則可以確定這兩個檔案将被清除。rm後面的exit指令是必需的,因為如果沒有它,程式将在接收到信号時停止的點繼續執行。

    1号信号一般用于hangup。要麼是有人故意挂斷了線路,要麼是線路不小心斷開了。

    在這種情況下,可以通過向信号清單中添加信号1來修改前面的trap,以删除兩個指定的檔案:

$ trap "rm $WORKDIR/work1$$ $WORKDIR/dataout$$; exit" 1 2
           

    現在,如果挂斷或CTRL+C鍵被按下,這些檔案将被删除。如果指定給trap的指令包含多個指令,則必須用引号括起來。還要注意,當trap指令執行時或接收到所列信号時,shell會同時掃描指令行。

    是以,在前面的示例中,WORKDIR和 $$ 的值将在執行trap指令時被替換。如果希望在接收到信号1或2時發生這種替換,可以将指令放在單引号内:

$ trap 'rm $WORKDIR/work1$$ $WORKDIR/dataout$$; exit' 1 2
           

忽略信号

    如果為trap列出的指令為空,則接收到的信号将被忽略。例如:

$ trap '' 2
           

    上例指定忽略中斷信号。在執行不希望被中斷的操作時,可能需要忽略某些信号。可以指定要忽略的多個信号,如下所示:

$ trap '' 1 2 3 15
           

    請注意,必須為要忽略的信号指定第一個參數。

    如果忽略一個信号,所有子shell也會忽略該信号。但是,如果指定接收信号時要執行的操作,則所有子shell在接收到該信号時仍将執行預設操作。

重置traps

    在更改了接收信号時要執行的預設操作之後,如果隻是省略第一個參數,就可以使用trap再次更改它:

$ trap 1 2
           

    這将把接收到的信号1或2的動作重置為預設值。

注意事項

    不要使用SIGKILL(9),除非您已經優雅地嘗試使用SIGTERM終止程序。

    關于trap的具體shell示例,可以參考Shell部分内置指令。

參考文檔

[1]Sahitya Maruvada.Understanding Linux Process Signals[EB/OL].https://medium.com/100-days-of-linux/understanding-linux-process-signals-53d44c85c706,2020-05-21.

[2]www.tutorialspoint.com.Unix/Linux - Signals and Traps[EB/OL].https://www.tutorialspoint.com/unix/unix-signals-traps.htm,2020-01-01.

[3]Andries Brouwer.Signals[EB/OL].https://www.win.tue.nl/~aeb/linux/lk/lk-5.html,2003-02-01.

[4]Wikipedia.Signal (IPC)[EB/OL].https://en.wikipedia.org/wiki/Signal_(IPC),2020-12-23.