gpt4 book ai didi

c - wait 和 waitpid 在 Linux 中返回时会阻塞 SIGCHLD 并解除阻塞吗?

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:46:24 25 4
gpt4 key购买 nike

这是我用来检查这个的代码:

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;
}

输出是:

before fork

before waitpid

handler 17

No child processes

after waitpid

after fork

所以,我认为 waitpid 会阻塞 SIGCHLD 并等待子进程终止,一旦子进程终止,它会做一些事情并在它返回之前解除对 SIGCHLD 的阻塞,这就是为什么我们看到“没有子进程” 错误和 "after waitpid" 是在 "handler 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) 的正常编码。 macOS 和 Linux 的信号编号不同;这是完全允许的。


为了让代码在 Linux(Centos 7 和 Ubuntu 16.04 LTS 用于测试)上编译,分别使用 GCC 4.8.5(几乎是过时的 - 当前版本是 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
$

我在第一个 header 之前添加了 #define _XOPEN_SOURCE 800,并使用了:

struct sigaction sig;
memset(&sig, '\0', sizeof(sig));

使用 GCC 4.8.5 初始化结构。这种恶作剧有时是避免编译器警告的痛苦必需品。我注意到,尽管 #define 是公开 POSIX 符号所必需的,但初始化程序 (struct sigaction sig = { 0 };) 已被 GCC 5.4.0 毫无问题地接受。

然后当我运行该程序时,我得到与 cong 非常相似的输出报告进入 comment :

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 有多重要;自第一次从 child 那里收集信息以来,该错误在循环的第二次迭代中是不可避免的。实际上,这里并不重要。一般而言,最好使用 waitpid(0, &status, WNOHANG) 或附近 — 根据上下文,0 而不是 WNOHANG可能会更好。

关于c - wait 和 waitpid 在 Linux 中返回时会阻塞 SIGCHLD 并解除阻塞吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43949142/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com