1

希望是一个简单的问题。我正在尝试同时学习 fork()、pipe() 和 waitpid() 并遇到一些问题。

if (pipe(myout)<0 || pipe(myin)<0 || pipe(myerr)<0) { perror("Couldn't make pipes"); return; }
int childpid=fork();
if (childpid==0) { //child
    fdopen(myout[1], "w");
    fdopen(myin[1], "r");
    fdopen(myerr[1], "w");
    dup2(myout[1],  1);
    dup2(myin[1], 0);
    dup2(myerr[1], 2);
    printf("This should be seen\n");
    fclose(stdout); fclose(stdin); fclose(stderr);
    sleep(10);
    _exit(0);
 } else { //parent, wait on child
    printf("parent, monitoring\n");
    sim_out=fdopen(myout[0], "r");
    sim_in=fdopen(myin[0], "w");
    sim_err=fdopen(myerr[0], "r");
    printf("have my fds\n");
    int status;
    do {
        int ch;
        if (read(myout[0], &ch, 1)>0)
            write(1, &ch, 1);
        else printf("no go\n");
            waitpid(childpid, &status, WNOHANG);
    } while (!WIFEXITED(status) && !WIFSIGNALED(status));
}

我越来越:

父母,监控有我的fds T

在程序退出之前——也就是说,循环只运行一次。我在下面有一个检查,它正在出现 WIFEXITED() 所以这个过程应该已经正常退出了。但令我困扰的是,在此之前有一个 sleep(10) ,并且这会立即发生 - 更不用说子进程在剩余的等待时间内保持运行。

显然,我从根本上误解了一些东西。我的期望是父循环会阻塞,直到它看到来自孩子的角色,阅读它,然后检查它是否还活着。当孩子还活着的时候,我当然没想到 waitpid() 会设置 WIFEXITED。

我哪里错了?

4

2 回答 2

5

我想我可以看到几个问题。我将尝试按出现的顺序提及它们。

  • 您应该检查指示错误fork的返回值-1(在这种情况下不会派生子节点)。
  • 您对泄漏资源的所有调用fdopen(取决于实现;关于 RHEL4 泄漏的调用)。他们返回一个FILE*你可以在fwrite等中使用的然后关闭fclose它们。但是你把那个价值扔掉了。您无需打开管道进行读/写。管道在创建时适用于此。
  • 孩子应该关闭它不使用的管道的末端。添加close (myin [1]); myin [1] = -1; close (myout [0]); myout [0] = -1; close (myerr [0]); myerr [0] = -1;
  • 我知道的dup2所有 Linux 变体在技术上都很好,但习惯上使用管道的第一个 fd 进行读取,另一个用于写入。因此,您dup2的 s 最好改为dup2 (myin [0], STDIN_FILENO); dup2 (myout [1], STDOUT_FILENO); dup2 (myerr [1], STDERR_FILENO);
  • 父母应该关闭它不使用的管道的末端。添加close (myin [0]); myin [0] = -1; close (myout [1]); myout [1] = -1; close (myerr [1]); myerr [1] = -1;
  • 您的主要问题:您检查孩子的退出代码可能未初始化status。但waitpid尚未被调用。您应该检查waitpid的退出代码,而不是评估status它是否返回childpid.

编辑

由于现在只有您实际需要的管道末端是打开的,因此操作系统会为您检测到损坏的管道。当孩子这样做时,管道会破裂fclose (stdout)。父级仍然可以继续读取管道中可能存在的所有数据,但之后read将返回零,表示管道损坏。

因此,您实际上可以省去对waitpid. 相反,您可以简单地等待read返回零。但是,这不是 100% 等效的,因为您的孩子在进入之前关闭了管道的末端sleep(导致父母在读取所有数据后继续进行),而该waitpid版本当然只在孩子实际死亡时继续.

于 2010-12-03T17:42:31.697 回答
0

有人回答了这个......我不知道它发生了什么。考虑到我是这里的新手,也许我以某种方式删除了它?如果是这样,我很抱歉,但他们有答案。

解决方案是不使用 WIF* 宏,除非 waitpid()>0,否则显然 0 被视为正常退出。我在我的代码中插入了一个检查,它现在可以工作了 - 感谢大家的编辑指针。

于 2010-12-03T17:51:21.453 回答