以下是我的代码:
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返回之前解锁,这就是为什么我们看到&#34 ;没有子进程&#34; 错误和&#34;在waitpid&#34; 之后是&#34;处理程序17&#34; ,我是对?如果不是,真相是什么?如何解释输出序列?是否有针对Linux或其他类似的规范进行检查?
答案 0 :(得分:3)
流程的退出信息只能收集一次。您的输出显示在代码位于waitpid()
时调用的信号处理程序,但处理程序调用{{1}}并收集子项的信息(您在不报告的情况下丢弃)。然后,当您回到wait()
时,已经收集了子退出状态,因此waitpid()
没有任何内容可以报告,因此“没有子进程”#39;错误。
这是您的计划的改编。它通过在信号处理函数中使用waitpid()
来滥用它,但它似乎工作,尽管如此,在运行macOS Sierra 10.12.4的Mac上进行测试(使用GCC 7.1.0进行编译)。
printf()
示例输出:
#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;
}
状态值0x7F00是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
的正常编码。来自Linux的macOS的信号编号不同;这完全是允许的。
要获得在Linux上编译的代码(用于测试的Centos 7和Ubuntu 16.04 LTS),使用GCC 4.8.5(几乎是antediluvian - 当前版本是GCC 7.1.0)和5.4.0,使用命令行:
_exit(127)
我在第一个标头之前添加了$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes \
> -Wstrict-prototypes -Wold-style-definition sg59.c -o sg59
$
,并使用了:
#define _XOPEN_SOURCE 800
使用GCC 4.8.5初始化结构。那种shenanigan偶尔是避免编译器警告的痛苦必要。我注意到尽管公开POSIX符号需要struct sigaction sig;
memset(&sig, '\0', sizeof(sig));
,但GCC 5.4.0接受初始值设定项(#define
)没有问题。
当我运行该程序时,我得到的输出与cong中comment报告的内容非常相似:
struct sigaction sig = { 0 };
确实很奇怪,在Linux上,进程发送了SIGCHLD信号,但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
无法在信号处理程序中等待它。这至少是违反直觉的。
我们可以讨论wait()
的第一个论点是waitpid()
而不是pid
的重要性。因为第一次从子节点收集信息,所以在循环的第二次迭代中错误是不可避免的。在实践中,这并不重要。一般情况下,最好使用0
或其左右 - 根据具体情况,waitpid(0, &status, WNOHANG)
代替0
可能会更好。