[英]Will wait and waitpid block SIGCHLD and unblock it when they return in Linux?
這是我的代碼來檢查這個:
void handler(int n) {
printf("handler %d\n", n);
int status;
if (wait(&status) < 0)
printf("%s\n", strerror(errno));
}
int main() {
struct sigaction sig;
sigemptyset(&sig.sa_mask);
sig.sa_handler = handler;
sig.sa_flags = 0;
sig.sa_restorer = NULL;
struct sigaction sigold;
sigaction(SIGCHLD, &sig, &sigold);
pid_t pid;
int status;
printf("before fork\n");
if ((pid = fork()) == 0) {
_exit(127);
} else if (pid > 0) {
printf("before waitpid\n");
if (waitpid(pid, &status, 0) < 0)
printf("%s\n", strerror(errno));
printf("after waitpid\n");
}
printf("after fork\n");
return 0;
}
輸出是:
在前叉
在waitpid之前
處理程序17
沒有孩子的過程
在waitpid之后
叉后
因此,我認為waitpid將阻止SIGCHLD並等待子進程終止,一旦子進程終止,它將執行某些操作並在返回之前解鎖SIGCHLD,這就是為什么我們看到“No child processes”錯誤並且“after waitpid”之后“處理程序17” ,我是對的? 如果不是,真相是什么? 如何解釋輸出序列? 是否有針對Linux的規范或類似的東西來檢查?
進程的退出信息只能收集一次。 您的輸出顯示在代碼處於waitpid()
調用的信號處理程序,但處理程序調用wait()
並收集子項的信息(在沒有報告的情況下丟棄)。 然后當你回到waitpid()
,已經收集了子退出狀態,因此沒有任何東西可供waitpid()
報告,因此“無子進程”錯誤。
這是您的計划的改編。 它通過在信號處理函數中使用printf()
來濫用它,但它似乎工作,盡管如此,在運行macOS Sierra 10.12.4(使用GCC 7.1.0編譯)的Mac上進行測試。
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
static void handler(int n)
{
printf("handler %d\n", n);
int status;
int corpse;
if ((corpse = wait(&status)) < 0)
printf("%s: %s\n", __func__, strerror(errno));
else
printf("%s: child %d exited with status 0x%.4X\n", __func__, corpse, status);
}
int main(void)
{
struct sigaction sig = { 0 };
sigemptyset(&sig.sa_mask);
sig.sa_handler = handler;
sig.sa_flags = 0;
sigaction(SIGCHLD, &sig, NULL);
pid_t pid;
printf("before fork\n");
if ((pid = fork()) == 0)
{
_exit(127);
}
else if (pid > 0)
{
printf("before waitpid\n");
int status;
int corpse;
while ((corpse = waitpid(pid, &status, 0)) > 0 || errno == EINTR)
{
if (corpse < 0)
printf("loop: %s\n", strerror(errno));
else
printf("%s: child %d exited with status 0x%.4X\n", __func__, corpse, status);
}
if (corpse < 0)
printf("%s: %s\n", __func__, strerror(errno));
printf("after waitpid loop\n");
}
printf("after fork\n");
return 0;
}
樣本輸出:
before fork
before waitpid
handler 20
handler: child 29481 exited with status 0x7F00
loop: Interrupted system call
main: No child processes
after waitpid loop
after fork
狀態值0x7F00是_exit(127)
的正常編碼。 來自Linux的macOS的信號編號不同; 這完全是允許的。
要獲得在Linux上編譯的代碼(用於測試的Centos 7和Ubuntu 16.04 LTS),使用命令行分別使用GCC 4.8.5(幾乎是antediluvian - 當前版本是GCC 7.1.0)和5.4.0:
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes \
> -Wstrict-prototypes -Wold-style-definition sg59.c -o sg59
$
我在第一個標頭之前添加了#define _XOPEN_SOURCE 800
,並使用了:
struct sigaction sig;
memset(&sig, '\0', sizeof(sig));
使用GCC 4.8.5初始化結構。 那種shenanigan偶爾是避免編譯器警告的痛苦必要。 我注意到雖然#define
是暴露POSIX符號所必需的,但GCC 5.4.0接受了初始化程序( struct sigaction sig = { 0 };
)而沒有任何問題。
當我運行該程序時,我得到的報告非常類似於cong報告的評論 :
before fork
before waitpid
handler 17
handler: No child processes
main: child 101681 exited with status 0x7F00
main: No child processes
after waitpid loop
after fork
確實很奇怪,在Linux上,進程發送了一個SIGCHLD信號,但wait()
不能在信號處理程序中等待它。 這至少是違反直覺的。
我們可以討論waitpid()
的第一個參數是pid
而不是0
waitpid()
; 因為第一次從子節點收集信息,所以在循環的第二次迭代中錯誤是不可避免的。 在實踐中,這並不重要。 一般來說,最好使用waitpid(0, &status, WNOHANG)
或其附近 - 根據上下文, 0
而不是WNOHANG
可能會更好。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.