signal 小知識

TLPI 長知識。

###signal 設定和 fork

fork 後會繼承 signal 全部設定, 而 exec 後會保留 ignore 的設定, 但因為 address space 不同, 會將有設過的 signal handler 改回 default handler(見 man 7 signal)。

###先送 SIGTERM 再送 SIGKILL 砍程式

若程式有照標準寫, 可能會有 SIGTERM 的 handler, 在收到這 signal 時做些清理動作 (砍暫存檔、釋放資源等), 再自我了結。而 SIGKILL 就直接掛了。所以, 先給人家一個機會掛得優雅一些, 若對方拒絕的話, 再狠一點直接掛了它。

附帶一提, ctrl + c 是送出 SIGINT。

###執行 signal handler 的時機

process 得先拿到 CPU 才能執行 signal handler, 換句話說, 若 process P 處於 stop 的狀態 (D、S、T), 或系統太忙撥不出時間給它, 即使已送出 SIGTERM 給 P 了, P 也無法處理。而送 signal 的一方只能由 signal system call 的返回值判斷 P 仍活著, 有成功送出 signal, 無法得知 P 何時能處理它。

特別的是, SIGKILL 一送出對方就掛了, 不用取得 CPU 再掛掉。沒看到有特別的文獻指出 SIGKILL 這點特別的行為。man 7 signal 只有指出「The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored」。就實用角度來看, 讓 SIGKILL、SIGSTOP 立即生效, 確實比較合理。

###SIGHUP 的行為

terminal 斷線 (hangup) 時, terminal 的主控 process 會收到 SIGHUP。而它的預設行為是終結程式, 所以關掉 terminal 時, 裡面的程式會直接結束。

bash 和 ksh 在結束的時候會送 SIGHUP 給背景程式, 若背景程式也沒寫 SIGHUP handler, 那它們也會一起結束。這是關掉 terminal, 全部程式會一起結束的原因。

要避免這個行為, 可用 nohup 執行程式, 它會做 I/O 重導、執行 signal (SIGHUP, SIG_IGN), 然後執行目標程式。或是用 bash 的 disown -h, 再放到背景跑, 或用 gdb attach process, 再輸入 handle SIGHUP nopass

###用 SIGQUIT 產生 core dump

在 terminal 按 ctrl + \ 時會送出 SIGQUIT 給前景程式, 這個 signal 的作用是要就該程式自我了結並產生 core dump。當然, 一些設定要先設好, 才會有 core dump

這有許多好處:

  • 程式進無窮迴圈時按 ctrl + , 再用 gdb 列 backtrace 看卡在那。雖說直接 用 gdb attach 也 ok。若高中練 ACM 知道這個方法, 可省下一些除錯時間 (遠目)。
  • 方便測試 core dump 設定, 不用另寫個小程式故意寫入不合法的位置。

书籍推荐