C关闭STDOUT永远运行

时间:2013-02-10 06:25:46

标签: c pipe infinite-loop

我正在编写一些涉及管道使用的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"

2 个答案:

答案 0 :(得分:1)

最小值处,您应该确保dup2函数返回新文件描述符而不是-1

总有可能会出现错误(例如,如果之前pipe()调用失败)。此外,绝对肯定你正在使用正确的索引(0和1) - 我之前一直被它咬过,这取决于你是在父进程还是子进程。


根据您的编辑,MADE PIPES是打印的最后一件事我并不感到惊讶。

当您尝试打印TEST 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描述符后(但在printf编辑之前),STDOUT中的dup返回代码,这意味着它失败。

来自ISO C11 7.20.6.3 The printf function

  

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

答案 1 :(得分:1)

多重要提,

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

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

关于你的编码风格还有很多其他的事情,你应该抓住它:) 你越早学会它就能节省你的时间。 :)

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

使用printf语句记录错误或作为调试方法,并且您正在更改终端FD的(STDOUT / STDIN / STDERR),更好地打开日志文件*NIX打开并写入错误/日志条目。

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