如何在 Bash 腳本中使用 Linux 信號

Linux 內核向進程發送關於它們應該對事件做出反應的信號。 表現良好的腳本優雅而穩健地處理信號,即使您按 Ctrl+C 也可以自行清理。 就是那樣。

信號和過程

信號是發送到腳本、程序和守護進程等進程的簡短、快速、單向消息。 他們告知流程已經發生的事情。 用戶可能按下了 Ctrl+C,或者應用程序可能試圖寫入它無權訪問的內存。

如果進程的作者預計某個信號可能會發送給它,它可以在程序或腳本中編寫一個例程來處理該信號。 這樣的例程稱為信號處理程序。 捕獲或捕獲信號並執行一些動作來響應它。

正如我們將看到的,Linux 使用許多信號,但從腳本的角度來看,您可能感興趣的信號只有一小部分。 特別是,在非平凡的腳本中,應該捕獲指示腳本關閉的信號(只要可能),並且應該執行正常關閉。

例如,創建臨時文件或打開防火牆端口的腳本可能有機會在關閉臨時文件或關閉端口之前刪除它們。 如果腳本在接收到信號的那一刻就死機,您的計算機可能會處於不可預知的狀態。

這就是您可以在自己的腳本中處理信號的方式。

了解標誌

一些 Linux 命令具有神秘的名稱。 捕獲信號的控件並非如此。 被稱為 trap . 我們也可以使用 trap -l (list) 選項向我們展示完整的列表Linux 使用的信號.

                      trap -l
                    

儘管我們的編號列表以 64 結尾,但實際上有 62 個信號。 缺少信號 32 和 33。它們是未在 Linux 上實現. 它們已被 gcc 編譯器實時管理線程。 從信號 34 開始的一切, SIGRTMIN 到第 64 點, SIGRTMAX 它們是實時信號。

您將在不同的類 Unix 操作系統上看到不同的列表。 在印第安納州公開賽例如,存在信號 32 和 33,以及一堆額外的信號,使總數達到 73。

Listado de señales en OpenIndiana con trap -l

信號可以通過名稱、編號或縮寫名稱來引用。 你的簡稱就是你的名字,沒有最初的“SIG”。

發出信號的原因有很多。 如果你能破譯它們,它們的目的就包含在它們的名字中。 信號的影響屬於以下類別之一:

  • 結果:進程終止。
  • 忽視:該信號不影響進程。 這是一個僅供參考的信號。
  • 中心:創建核心轉儲文件。 這通常是因為進程以某種方式違反了,例如內存違反。
  • 停止:該過程停止。 也就是說,它暫停了,沒有完成。
  • 繼續:告訴停止的進程繼續執行。

這些是您最常發現的跡象。

  • 訂閱: 信號 1. 與遠程主機(如 SSH 服務器)的連接意外斷開或用戶已註銷。 收到此信號的腳本可以成功終止,也可以選擇嘗試重新連接到遠程主機。
  • 繼續:信號 2。用戶按下組合鍵 Ctrl+C 以強制終止進程。 關上 kill 該命令已與信號 2 一起使用。從技術上講,這是一個中斷信號,而不是終止信號,但沒有信號處理程序的中斷腳本通常會終止。
  • 下一步移除: 信號 3. 用戶按下了 Ctrl+D 組合來強制關閉進程,或者 kill 該命令已與信號 3 一起使用。
  • SIGFPE:信號 8。進程試圖執行非法(不可能)的數學運算,例如除以零。
  • 隱身:信號 9。這是相當於斷頭台的信號。 你無法捕捉或忽略它,它會立即發生。 該過程立即終止。
  • SIGTERM: Signal 15. 這是考慮最多的版本 SIGKILL . SIGTERM 它還告訴進程終止,但它可能會卡住並且進程可能會在退出之前運行其清理進程。 這允許正常關閉。 這是由 kill 領域。

命令行上的信號

捕獲信號的一種方法是使用 trap 帶有信號的編號或名稱,以及在收到信號時您希望發生的響應。 我們可以在終端窗口中演示這一點。

該命令捕獲 SIGINT 符號。 答案是在終端窗口打印一行文本。 我們正在使用 -e (啟用轉義)選項 echo 那麼我們可以使用“ n ” 格式說明符。

                      trap 'echo -e "+c Detected."' SIGINT
                    

Trapping Ctrl+C en la línea de comando

每次按下 Ctrl+C 組合時,都會打印我們的文本行。

要查看信號中是否存在陷阱,請使用 -p (打印陷阱)選項。

                      trap -p SIGINT
                    

Comprobación de si hay una trampa en una señal

穿著 trap 沒有選項是一樣的。

要將信號重置為正常狀態而不進行陷印,請使用破折號“ - ”以及被捕獲信號的名稱。

                      trap - SIGINT
                    
                      trap -p SIGINT
                    

Eliminar una trampa de una señal

沒有出路 trap -p 該命令指示該信號上沒有配置陷阱。

腳本中的陷阱信號

我們可以使用相同的通用格式 trap 腳本中的命令。 該腳本捕獲三個不同的信號, SIGINT , SIGQUIT SIGTERM .

                      #!/bin/bash

trap "echo I was SIGINT terminated; exit" SIGINT
trap "echo I was SIGQUIT terminated; exit" SIGQUIT
trap "echo I was SIGTERM terminated; exit" SIGTERM

echo $$
counter=0

while true
do 
  echo "Loop number:" $((++counter))
  sleep 1
done
                    

trap 聲明位於腳本的頂部。 請注意,我們已經包括 exit 命令在對每個信號的響應中。 這意味著腳本會對信號做出反應,然後退出。

將文本複製到編輯器中並將其保存到名為“simple-loop.sh”的文件中,並使用 chmod 領域。 如果您想在自己的計算機上關注本文中的所有腳本,則需要對它們執行此操作。 只需在每種情況下使用適當的腳本名稱即可。

                      chmod +x simple-loop.sh
                    

Haciendo un script ejecutable con chmod

腳本的其餘部分非常簡單。 我們需要知道腳本的進程 ID,因此腳本會將其回顯給我們。 這 $$ 該變量包含腳本的進程 ID。

我們創建一個名為 counter 並將其重置為零。

while 除非強制停止,否則循環將永遠運行。 它增加了 counter 變量,在屏幕上重複它並休眠一秒鐘。

讓我們運行腳​​本並發送不同的信號。

                      ./simple-loop.sh
                    

Un script que lo identifica ha sido terminado con Ctrl+C

當我們按下“Ctrl + C”時,我們的消息將打印在終端窗口中,腳本結束。

讓我們再次運行它並發送 SIGQUIT 信號使用 kill 領域。 我們將不得不從另一個終端窗口執行此操作。 您將不得不使用您自己的腳本報告的進程 ID。

                      ./simple-loop.sh
                    
                      kill -SIGQUIT 4575
                    

Un script que lo identifica ha sido terminado con SIGQUIT

正如預期的那樣,腳本報告信號到達然後終止。 最後,為了證明這一點,我們將再次使用 SIGTERM 符號。

                      ./simple-loop.sh
                    
                      kill -SIGTERM 4584
                    

Un script que lo identifica ha sido terminado con SIGTERM

我們已經驗證我們可以在一個腳本中捕獲多個信號並獨立地對它們做出反應。 將所有這些從有趣變為有用的步驟是添加信號處理程序。

處理腳本中的信號

我們可以將響應字符串替換為腳本中的函數名稱。 這 trap 然後,該命令在檢測到信號時調用該函數。

將此文本複製到編輯器中並將其保存為名為“grace.sh”的文件,並使其可執行 chmod .

                      #!/bin/bash

trap graceful_shutdown SIGINT SIGQUIT SIGTERM

graceful_shutdown()
{
  echo -e "nRemoving temporary file:" $temp_file
  rm -rf "$temp_file"
  exit
}

temp_file=$(mktemp -p /tmp tmp.XXXXXXXXXX)
echo "Created temp file:" $temp_file

counter=0

while true
do 
  echo "Loop number:" $((++counter))
  sleep 1
done
                    

該腳本為三個不同的信號設置了一個陷阱: SIGHUP , SIGINT SIGTERM ——使用單個 trap 陳述。 答案是名字 graceful_shutdown() 功能。 每次接收到三個捕獲信號之一時,都會調用該函數。

該腳本在“/tmp”目錄中創建一個臨時文件,使用 mktemp . 文件名模板是“tmp.XXXXXXXXXX”,所以文件名是“tmp”。 後跟十個隨機字母數字字符。 文件名在屏幕上重複。

腳本的其餘部分與上面相同,帶有 counter 變量和無窮大 while 圓圈。

                      ./grace.sh
                    

Una secuencia de comandos que realiza un cierre correcto al eliminar un archivo temporal

當信號被發送到導致文件關閉的文件時 graceful_shutdown() 該函數被調用。 這將刪除我們唯一的臨時文件。 在現實世界的情況下,您可以執行腳本所需的任何清理工作。

此外,我們將所有捕獲的信號分組並使用單個函數處理它們。 您可以單獨捕獲信號並將它們發送到您自己的專用控制器功能。

複製此文本並將其保存到名為“triple.sh”的文件中,並使用 chmod 領域。

                      #!/bin/bash

trap sigint_handler SIGINT
trap sigusr1_handler SIGUSR1
trap exit_handler EXIT

function sigint_handler() {
  ((++sigint_count))

  echo -e "nSIGINT received $sigint_count time(s)."

  if [[ "$sigint_count" -eq 3 ]]; then
    echo "Starting close-down."
    loop_flag=1
  fi
}

function sigusr1_handler() {
  echo "SIGUSR1 sent and received $((++sigusr1_count)) time(s)."
}

function exit_handler() { 
  echo "Exit handler: Script is closing down..."
}

echo $$
sigusr1_count=0
sigint_count=0
loop_flag=0

while [[ $loop_flag -eq 0 ]]; do
  kill -SIGUSR1 $$
  sleep 1
done
                    

我們在腳本頂部定義了三個陷阱。

  • 陷阱 SIGINT 並且有一個控制器叫做 sigint_handler() .
  • 第二個捕獲一個稱為 SIGUSR1 並使用一個名為 sigusr1_handler() .
  • 第三個陷阱抓住了 EXIT 符號。 該信號在腳本關閉時由腳本本身發出。 設置信號處理程序 EXIT 意味著您可以配置一個在腳本結束時始終調用的函數(除非被信號殺死 SIGKILL )。 我們的控制器被稱為 exit_handler() .

SIGUSR1 SIGUSR2 它們是提供的信號,以便您可以將自定義信號發送到您的腳本。 您如何解釋和對它們做出反應完全取決於您。

暫時不考慮信號處理程序,腳本的主體應該看起來很熟悉。 它在終端窗口中呈現進程 ID 並創建一些變量。 多變的 sigusr1_count 記錄次數 SIGUSR1 被操縱和 sigint_count 記錄次數 SIGINT 被處理了。 這 loop_flag 變量設置為零。

while 循環不是無限循環。 如果 loop_flag 變量設置為任何非零值。 每轉一圈 while 循環使用 kill 發送 SIGUSR1 向此腳本發送信號,將其發送到腳本的進程 ID。 腳本可以向自己發送信號!

sigusr1_handler() 該功能增加了 sigusr1_count 變量並向終端窗口發送消息。

每次他 SIGINT 接收到信號時, siguint_handler() 該功能增加了 sigint_count 變量並將其值回顯到終端窗口。

如果他 sigint_count 變量等於三,則 loop_flag 該變量設置為 1,並向終端窗口發送一條消息,通知用戶關閉過程已啟動。

因為 loop_flag 不再等於零,則 while 循環結束,腳本結束。 但是這個動作會自動提高 EXIT 標誌和 exit_handler() 該函數被調用。

                      ./triple.sh
                    

Un script que usa SIGUSR1, que requiere tres combinaciones de Ctrl+C para closey captar la señal de SALIDA al apagar

連續按三下 Ctrl+C 後,腳本結束並自動調用 exit_handler() 功能。

閱讀標誌

通過捕獲信號並在簡單的控制器函數中處理它們,您可以讓 Bash 腳本自行排序,即使它們意外終止。 這為您提供了一個更乾淨的文件系統。 它還可以防止您下次運行腳本時出現不穩定,並且根據腳本的用途,它甚至可以防止安全漏洞。

如何使用 Lynis 審核 Linux 系統的安全性

相關文章