顯示具有 signal 標籤的文章。 顯示所有文章
顯示具有 signal 標籤的文章。 顯示所有文章

2013年5月30日 星期四

linux thread 與 signal

雖然是基礎知識, 一陣子沒用大概會忘記, 備忘一下重點:

  • 在 multi-thread 情況下, signal() 的行為是未定義, 必須用 sigaction() 註冊 signal
  • 用 kill 送 signal 給 process 後, process 內任何一個 thread 都有可能執行 signal handler。若不希望特定的 thread 收到 signal, 可用 pthread_sigmask() 改變特定 thread 的行為, 或是用 pthread_kill() 送出 signal 給指定的 thread
  • SIGSEGV 會送給發生 segmentation fault 的 thread。所以有 core dump 的時候, gdb 可以正確找出那個 thread 造成 segfault

參考資料:

2012年5月2日 星期三

SIGCHLD 和 zombie

關於 zombie

  • zombie: ps 列的 status 為 Z, 表示 process 已結束, 資源也釋放了, 等待 parent process 呼叫 wait/waitpid 回收它的 exit status, 裡面包含 exit code
  • zombie 是無法被 kill 的, 僵屍無敵啊!!
  • zombie 會占據極小的系統資源, 如可 forked 的 process 數
  • 不註冊 SIGCHLD = 註冊 signal handler 為 SIG_IGR => OS 會在 child process 掛掉時直接回收, 不會變 zombie

關於 SIGCHLD 收發時機

  • 即使 child process 收到 SIGKILL, 它也不會當下就掛掉而讓 parent process 在當下收到 SIGCHLD。process 得等取得 CPU 時才會「處理」 SIGKILL 而掛掉。總之, 系統不保證 SIGCHLD 收到的時間
  • child process 正式掛掉後, 當 parent process 取得 CPU 時, 系統會盡快讓它收到 SIGCHLD。換句話說, 若 parent process 被停住的期間「收到」 SIGCHILD, resume 後會立即收到 SIGCHILD
  • SIGCHLD 如同其它 signal, 不會被 queued, 只有保證在有註冊 handler 的情況下, 至少會呼叫一次 handler (細節說明請查: signal mask/block )
  • 註冊 handler 前收到 SIGCHLD 的話, 不同系統作法不同, 可能會補送也可能不會補送

關於 handler

  • signal handler 會被繼承 (見 man fork 開頭), 所以若 A 有註冊 SIGCHLD 則 A 的 child process 和後續 grandchild process 都有 SIGCHLD handler。寫 handler 時要注意是否能沿用下去, 或是明確在每次 fork 後重設一次 child process 的 signal handler
  • 標準寫法如下, status 裡會記錄許多有用資訊, 見 man waitpid:
int status;
while ((pid=waitpid(-1, &status, WNOHANG)) > 0) {
    // Do something
}

2012年2月1日 星期三

從 /proc/PID/status 了解執行中程式處理 signal 的方式

man proc 可得知在 /proc/PID/status 裡有給人讀的 process 資訊, 其中 SigXXX 是關於 signal 的處理方式, 比方 SigIgn 表示忽略該 signal; SigCgt 表示有註冊自己的 signal handler。

比較需要花點心思的地方, 在於怎麼讀 signal mask。這裡有提到解碼方式, 每個數字用四個 bit 表示四個號碼, 從最右側開始遞增。

以這個程式為例:

#include <stdio.h>
#include <signal.h>

void handler(int signum) {
}

int main(void) {
    signal(SIGQUIT, SIG_IGN);
    signal(SIGHUP, handler);
    signal(SIGPIPE, handler);
    while (1) ;
    return 0;
}

它的 /proc/PID/status 如下:

SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000004
SigCgt: 0000000000001001

表示 3 號 signal (SIGQUIT) 設為 ignore (1, 2, 4 -> 4 表示第三個號碼), 1 號和 13 號 (SIGHUP 和 SIGPIPE) 有註冊 handler。signal 的號碼可從 man 7 signal 得知, 或在 include signal.h 後, 用 gcc -E -dD 展開看也可以。

2012-02-02 更新

Scott 在留言提供正式的解釋, 附註在此:

這是用 16 進位顯示一個 64 bit 的 bitmask,每個 bit 對應到一個 signal。 bits 從 0 開始編號而 Linux signals 從 1 開始編號,故 bit 0 代表 1 號 signal (SIGHUP), bit 1 代表 2 號 signal (SIGINT)。

原本寫的時候想不起來正式的說法, 想說就隨手寫個「白話版」吧。原本的「白話版」也留著, 有不同描述方式, 應該多少會有幫助吧。

2012年1月27日 星期五

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 設定, 不用另寫個小程式故意寫入不合法的位置。

在 Fedora 下裝 id-utils

Fedora 似乎因為執行檔撞名,而沒有提供 id-utils 的套件 ,但這是使用 gj 的必要套件,只好自己編。從官網抓好 tarball ,解開來編譯 (./configure && make)就是了。 但編譯後會遇到錯誤: ./stdio.h:10...