gpt4 book ai didi

c - 管道保证在 child 退出后关闭

转载 作者:太空宇宙 更新时间:2023-11-03 23:38:28 26 4
gpt4 key购买 nike

在下面的代码中,依靠read() 失败 来检测子进程的终止是否安全?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(void)
{
int pipefd[2];
pipefd[0] = 0;
pipefd[1] = 0;
pipe(pipefd);

pid_t pid = fork();

if (pid == 0)
{
// child
close(pipefd[0]); // close unused read end
while ((dup2(pipefd[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {} // send stdout to the pipe
while ((dup2(pipefd[1], STDERR_FILENO) == -1) && (errno == EINTR)) {} // send stderr to the pipe
close(pipefd[1]); // close unused write end

char *argv[3];
argv[0] = "worker-app";
argv[1] = NULL;
argv[2] = NULL;
execvp("./worker-app", argv);
printf("failed to execvp, errno %d\n", errno);
exit(EXIT_FAILURE);
}
else if (pid == -1)
{
}
else
{
// parent
close(pipefd[1]); // close the write end of the pipe in the parent

char buffer[1024];
memset(buffer, 0, sizeof(buffer));
while (1) // <= here is it safe to rely on read below to break from this loop ?
{
ssize_t count = read(pipefd[0], buffer, sizeof(buffer)-1);
printf("pipe read return %d\n", (int)count);
if (count > 0)
{
printf("child: %s\n", buffer);
}
else if (count == 0)
{
printf("end read child pipe\n", buffer);
break;
}
else if (count == -1)
{
if (errno == EINTR)
{ continue;
}
printf("error read child pipe\n", buffer);
break;
}
}

close(pipefd[0]); // close read end, prevent descriptor leak

int waitStatus = 0;
waitpid(pid, &waitStatus, 0);
}

fprintf(stdout, "All work completed :-)\n");
return EXIT_SUCCESS;
}

我应该在 while(1) 循环中添加一些东西来检测子进程终止吗?可能会发生什么特定情况并破坏此应用程序?

下面的一些改进想法。但是我会浪费 CPU 周期吗?

  1. 使用带特殊参数 0 的 kill 不会终止进程,只是检查它是否有响应:if (kill(pid, 0)) { 中断;/* child 退出 */};/* 如果sig为0,则不发送信号,但仍进行错误检查;这可用于检查进程 ID 或进程组 ID 是否存在。 https://linux.die.net/man/2/kill */

  2. 在 while(1) 循环中使用非阻塞的 waitpid 来检查 child 是否已经退出。

  3. 使用 select() 检查管道可读性以防止 read() 可能挂起?

谢谢!

最佳答案

关于你的想法:

  • 如果 child 产生自己的 child ,read() 将不会返回 0,直到它的所有后代都死亡或关闭 stdout 和 stderr。如果没有,或者如果 child 总是比它的所有后代都长寿,那么只需等待 read() 返回 0 就足够了,永远不会造成问题。
  • 如果 child 死了,但 parent 还没有wait(2)处理它,那么kill(pid, 0)会成功,就好像 child 死了一样仍然存在(至少在 Linux 上),因此这不是来自父程序的有效检查。
  • 非阻塞 waitpid() 本身似乎可以解决 child 有自己的 child 的问题,但实际上会引入微妙的竞争条件。如果 child 在 waitpid() 之后但在 read() 之前退出,那么 read() 将阻塞直到后代退出。
  • 就其本身而言,如果您以阻塞方式使用 select(),它并不比调用 read() 好多少。如果您以非阻塞方式使用 select(),您最终只会在循环中消耗 CPU 时间。

我会做什么:

  • 为 SIGCHLD 添加一个空操作信号处理函数,以便它在发生时导致 EINTR。
  • 在开始循环之前阻止父级中的 SIGCHLD。
  • 使用非阻塞的read,并使用pselect(2) 阻塞以避免CPU 永远旋转。
  • pselect 期间,传入一个没有阻止 SIGCHLD 的 sigset_t,以保证在它最终被发送时会导致 EINTR。
  • 在循环的某处,执行非阻塞 waitpid(2),并适本地处理它的返回。 (确保在阻止 SIGCHLD 之后但在第一次调用 select 之前至少执行一次此操作,否则您将遇到竞争条件。)

关于c - 管道保证在 child 退出后关闭,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52858818/

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