0

我正在编写一些涉及使用管道的 C 代码。为了让子进程使用我的管道而不是 STDOUT 进行输出,我使用了以下几行:

    close(STDOUT);
    dup2(leftup[1], STDOUT);

但是,它似乎进入某种无限循环或挂在这些线上。当我摆脱它时close,它会挂起dup2

奇怪的是,同样的想法也适用于 STDIN 的前一行:

close(STDIN);
dup2(leftdown[0], STDIN);

什么可能导致这种行为?

编辑:只是为了清楚......

#define STDIN   0
#define STDOUT  1

编辑 2:这是一个精简的示例:

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

#define STDIN   0
#define STDOUT  1

main(){
    pid_t child1 = 0;
    int leftdown[2];
    if (pipe(leftdown) != 0)
        printf("ERROR");
    int leftup[2];
    if (pipe(leftup) != 0)
        printf("ERROR");

    printf("MADE PIPES");

    child1 = fork();
    if (child1 == 0){
        close(STDOUT);
        printf("TEST 1");
        dup2(leftup[1], STDOUT);
        printf("TEST 2");
        exit(0);
    }
    return(0);  
}

"TEST 1"条线永远达不到。唯一的输出是"MADE PIPES"

4

2 回答 2

1

至少您应该确保该dup2函数返回新的文件描述符而不是-1.

它总是有可能给你一个错误(例如,如果pipe()之前调用失败)。此外,请绝对确定您使用的是正确的索引(0 和 1)——我之前被这个问题困扰过,这取决于您是在父进程还是子进程中。


根据您的编辑,我一点也不惊讶这MADE PIPES是最后一件事。

当您尝试 printTEST 1时,您已经关闭了STDOUT描述符,因此它将无处可去。

当您尝试打印TEST 2时,您已经dup编辑了STDOUT描述符,以便将其发送给父级,但您的父级不会读取它。

如果您将分叉代码更改为:

child1 = fork();
if (child1 == 0){
    int count;
    close(STDOUT);
    count = printf("TEST 1\n");
    dup2(leftup[1], STDOUT);
    printf("TEST 2 (%d)\n", count);
    exit(0);
} else {
    char buff[80];
    read (leftup[0], buff, 80);
    printf ("%s\n", buff);
    sleep (2);
}

您会看到该TEST 2 (-1)行是由父级输出的,因为它是通过管道读取的。里面是您在关闭描述符后(但在您编辑它之前)尝试在孩子中的-1返回代码,这意味着它失败了。printfSTDOUTdup

来自ISO C11 7.20.6.3 The printf function

printf函数返回传输的字符数,如果发生输出或编码错误,则返回负值。

于 2013-02-10T06:28:42.503 回答
1

多事要提,

当您使用 fork 时,它会导致几乎完整的父进程副本。stdout这还包括为标准输出流设置的缓冲区。流将stdout保存数据,直到缓冲区已满或明确请求从缓冲区/流中刷新数据。现在正因为如此,现在你已经"MADE PIPES"坐在缓冲区中了。当您关闭STDOUTfd 并printf用于将数据写入终端时,它只会将您的"TEST 1"和传输"TEST 2"stdout缓冲区中,并且不会导致任何错误或崩溃(由于缓冲区足够)。因此,即使在复制pipefd on之后STDOUT,由于缓冲输出printf甚至没有触及管道写入结束。最重要的是,请仅使用一组 API,即 *NIX 或标准 C lib 函数。确保你很好地理解了这些库,因为它们经常为某种优化而耍花招。

现在,要提到的另一件事是,确保在适当的过程中关闭管道的适当末端。这意味着如果说 pipe-1 用于从父级到子级进行通信,那么请确保您关闭父级中的读取端并在子级中写入端。否则,由于与文件描述符相关的引用计数,您的程序可能会挂起,您可能会认为在子进程中关闭读取端意味着管道读取端已关闭。但是,当您不关闭父级中的读取端时,您对管道的读取端有额外的引用计数,最终管道将永远不会关闭。

关于你的编码风格还有很多其他的东西,你应该更好地掌握它:) 你越早学会它就会节省你的时间。:)

错误检查是绝对重要的,至少使用 assert 来确保你的假设是正确的。

在使用printf语句记录错误或作为调试方法并且您正在更改终端 FD 时(STDOUT / STDIN / STDERR),最好打开一个带有*NIXopen 的日志文件并将错误/日志条目写入其中。

最后,使用strace实用程序将对您有很大帮助。此实用程序将允许您跟踪执行代码时执行的系统调用。这是非常直接和简单的。如果您有正确的权限,您甚至可以将其附加到执行过程中。

于 2013-02-10T10:41:43.103 回答