0

我目前正在尝试了解dup2和 C 管道的组合,但即使是最简单的程序似乎也不起作用。在阅读示例代码时,我已经对它们何时关闭管道末端以及应在何处打印输出感到非常困惑。

有时写端是关闭的,即使稍后应该生成一行输出,该输出应该进入管道。在其他示例中,未使用的一端是封闭的(这对我来说更有意义)。

然后,我不明白什么时候dup2应该执行。我想它应该在我想要重定向的输出之前出现,但我感觉我今天也看到了不同的情况。

所以最后我想出了这个小测试,printffflush每一行中,没有任何东西通过管道重定向。为什么?我究竟做错了什么?

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int out_pipe[2];
    char *output[101];

    if (pipe(out_pipe) != 0) {
        perror("pipe()");
        exit(1);
    }

    printf("Hello");
    fflush(stdout);

    dup2(out_pipe[1], STDOUT_FILENO);
    printf("Hello");
    fflush(stdout);

    close(out_pipe[1]);
    printf("Hello");
    fflush(stdout);

    read(out_pipe[0], output, 100);
    close(out_pipe[0]);

    printf("PIPE: %s", output);
    fflush(stdout);

    return 0;
}
4

1 回答 1

4
  1. printf()用换行符结束您的消息;这fflush()仍然是一个好主意,因为您将要更改标准输出的位置,尽管如果程序的标准输出要发送到终端,通常不需要这样做。如果标准输出要发送到一个文件并且fflush()没有到位,那么您将获得三个"Hello\n"写入管道的副本。

  2. 当您将标准输出更改为管道时,您的消息确实会写入管道。

  3. 当您关闭写入文件描述符时,您不会遇到任何问题。然后,您将一秒钟写入Hello管道。您需要它fflush()来确保标准 I/O 包实际上已将其缓冲数据写入管道。

  4. 然后,您从管道中读取到output缓冲区。您应该检查您读取的字节数,因为字符串不会以空值结尾。您应该读取 10 个字节(当消息中没有任何换行符时)。

  5. PIPE:然后,您再次使用前缀写入管道。

要修复,请将消息写入标准错误。

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int out_pipe[2];
    char output[101];

    if (pipe(out_pipe) != 0) {
        perror("pipe()");
        exit(1);
    }

    printf("Hello\n");
    fflush(stdout);

    dup2(out_pipe[1], STDOUT_FILENO);
    printf("Hello\n");
    fflush(stdout);

    close(out_pipe[1]);
    printf("Hello\n");
    fflush(stdout);

    int n = read(out_pipe[0], output, sizeof(output));
    close(out_pipe[0]);

    fprintf(stderr, "PIPE: %.*s\n", n, output);

    return 0;
}

请注意,我将 的定义output从一个数组更改char *为一个简单的char. 通过更改,我得到了输出:

$ ./pipe3
Hello
PIPE: Hello
Hello

$

那是因为我在写入管道的消息中以及以标准错误结尾的格式字符串中包含了换行符。


是否有可能“重新启用”标准输出?

是的; 只需在使用之前为标准输出保留原始文件描述符dup2()的副本,然后在完成管道后恢复副本。

我删除了两个主要fflush()调用,示例输出演示了终端和文件输出之间的区别:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int out_pipe[2];
    char output[101];
    int old_stdout;

    if (pipe(out_pipe) != 0) {
        perror("pipe()");
        exit(1);
    }

    printf("Hello\n");

    old_stdout = dup(STDOUT_FILENO);

    dup2(out_pipe[1], STDOUT_FILENO);
    printf("Hello\n");

    close(out_pipe[1]);
    printf("Hello\n");
    fflush(stdout);

    int n = read(out_pipe[0], output, sizeof(output));
    close(out_pipe[0]);

    dup2(old_stdout, STDOUT_FILENO);
    printf("PIPE: %d <<%.*s>>\n", n, n, output);

    return 0;
}

示例输出:

$ ./pipe3Hello
PIPE: 12 <<Hello
Hello
>>
$./pipe3 > output
'pipe3' is up to date.
$ cat output
PIPE: 18 <<Hello
Hello
Hello
>>
$

如果删除剩余fflush()的 ,程序将挂起。管道中没有任何内容(因为标准 I/O 尚未刷新其缓冲区,因为它未满且输出不再是终端),但管道已打开以进行写入,因此内核认为该输入可能会出现在它上面——如果只有打开管道进行写入的程序没有在管道的读取端等待输入出现。程序本身陷入僵局。

于 2013-07-26T16:11:54.877 回答