我有一個相當大的應用程式正在處理。作為其工作的一部分,它會產生一些子進程並需要監視它們的狀態(正在運行、崩潰)。
SIGCHLD
透過設定使用的訊號處理程序來偵測子進程死亡signal(2)
。前段時間我把它移轉到了signalfd(2)
.我所做的很簡單:
- 刪除了訊號處理程序
SIGCHLD
- 阻止
SIGCHLD
並創建一個signalfd(2)
捕獲SIGCHLD
我的問題是我創建的文件描述符似乎沒有捕獲SIGCHLD
.但是,如果我忽略該描述符上的呼叫的返回值read(2)
並呼叫waitpid(-1, &status, WNOHANG)
I能取得有關退出的子進程的資訊。所以看起來通知已發送,但我的signalfd(2)
描述符只是忽略了它。
我確保程式中恰好有一處在描述符read(2)
上被調用signalfd(2)
,恰好有一處被waitpid(2)
調用,並且恰好有一處設置了信號處理。
設定代碼如下所示:
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask, nullptr);
int signal_fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
if (signal_fd == -1) {
/* log failure and exit */
} else {
/* log success */
}
讀取程式碼如下所示:
signalfd_siginfo info;
memset(&info, 0, sizeof(info));
if (read(signal_fd, &info, sizeof(info)) == -1) {
/*
* Log failure and return.
* The file descriptor *always* returns EAGAIN, even in
* presence of dead child processes.
*/
return;
}
if (info.ssi_signo == SIGCHLD) {
int status = 0;
int child = waitpid(-1, &status, WNOHANG);
/*
* Process result of waitpid(2). The call is successful even if
* the read of signalfd above returned an error.
*/
}
我究竟做錯了什麼?
編輯:問題是,即使有已死亡的子進程準備好進行-ed,read(2)
也會失敗,這意味著 a必須已傳遞到我的主進程。我知道這可能會返回非阻塞文件描述符,並且代碼說明了這一點。EAGAIN
waitpid(2)
SIGCHLD
read(2)
EAGAIN
答案1
當從訊號處理遷移時,基於signal(2)
或sigaction(2)
改變signalfd(2)
您接收訊號的方式。舊的方式讓訊號暢通無阻,新的方式則需要阻塞訊號。
如果您不想在某些程式碼區域中受到訊號幹擾,則需要封鎖它們:
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGFOO);
pthread_sigmask(SIG_BLOCK, &mask, nullptr);
{
/* not-to-be-disturbed code here */
}
這需要你稍後解鎖他們,因為否則將signal(2)
無法sigaction(2)
接住他們。
{
/* not-to-be-disturbed code here */
}
pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);
然而,因為signalfd(2)
訊號必須保持阻塞。如果您有一個長期被忽視的程式碼路徑,您很少查看它,並且它遵循舊的方式,即阻止和解除阻止某些信號,那麼它可能會破壞您從signalfd(2)
.
長話短說在遷移到 時,檢查您的程式碼是否有對 、 等的任何調用,signal(2)
以sigaction(2)
檢查您是否沒有忘記某些與訊號掩碼混淆的程式碼路徑。pthread_sigmask(2)
signalfd(2)
(兩年半後可能有點晚了,但也許答案會對某人有所幫助。)
答案2
您的read(2)
回傳是因為您使用(與)EAGAIN
以非阻塞模式開啟檔案。signalfd(..., SFD_NONBLOCK | ...)
SFD_NONBLOCK
O_NONBLOCK
如果您想對檔案描述符進行阻塞讀取,請不要開啟檔案描述符或將其設定為非阻塞模式。