C Linux管道读取无序。缺失和重复输出

时间:2017-11-11 23:25:21

标签: c linux io

我正在开发一个应用程序,它需要启动任意子进程并在它们运行时读取它们的输出。将它视为终端仿真器可能是有用的,因为它必须能够在整个生命周期内多次运行子进程并回读它们的输出,并且我希望它以与终端显示它相同的方式回读它们的输出

我也无法控制子进程的代码,所以我不能通过在子进程代码中更频繁地刷新来解决这个问题,尽管在每次打印之后刷新似乎对大多数(所有?)都有帮助问题。

我设置了以下显示问题的小型演示,我无法在这个小型演示中修复它:

设置:

exerciser - 一个打印一些东西的程序,并启动一个也打印一些东西的子进程。他们的打印设置是这样的,这两个进程可以缓冲“A”,除非在fork之前刷新stdout。

reader - 启动子进程并设置管道以读取其输出。立即打印从孩子那里读取的所有内容。孩子参加锻炼计划。

----终端输出----

<...>/pipe-testing$ ./exerciser
A: 10
B: 42
C: 42
C: 10
<...>/pipe-testing$ ./reader
READER...
A: 10
B: 42
C: 42
A: 10
C: 10

---- ---- exerciser.cpp

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

int main(){
    int n = 10, status = 0;
    fprintf(stdout, "A: %d\n", n);
    if (fork() == 0){
        n = 42;
        fprintf(stdout, "B: %d\n", n);
    }
    else{
        wait(&status);
    }
    fprintf(stdout, "C: %d\n", n);
    return(0);
}

---- ---- reader.cpp

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

int main(){
    fprintf(stdout, "READER...\n");
    fflush(stdout);

    int pipe_fds[2];
    if (pipe(pipe_fds) == -1){
        fprintf(stdout, "failed to make pipe\n");
        return(0);
    }

    pid_t child_pid = fork();
    if (child_pid == -1){
        fprintf(stdout, "failed to fork\n");
        return(0);
    }

#define PIPE_READ 0
#define PIPE_WRITE 1

    if (child_pid == 0){
        close(pipe_fds[PIPE_READ]);
        dup2(pipe_fds[PIPE_WRITE], STDOUT_FILENO);
        dup2(pipe_fds[PIPE_WRITE], STDERR_FILENO);

        char *argv[] = {
            "sh",
            "-c",
            "./exerciser",
            0
        };

        if (execv("/bin/sh", argv) == -1){
            fprintf(stdout, "failed to execv\n");
            return(0);
        }
    }
    else{
        close(pipe_fds[PIPE_WRITE]);

        char buffer[1024];
        int capacity = sizeof(buffer);
        for (;;){
            ssize_t num = read(pipe_fds[PIPE_READ], buffer, capacity);
            if (num == -1){
                fprintf(stdout, "read error\n");
                return(0);
            }
            else if (num == 0){
                break;
            }
            else{
                fprintf(stdout, "%.*s", (int)num, buffer);
                fflush(stdout);
            }
        }
    }

    return(0);
}

1 个答案:

答案 0 :(得分:2)

  

我也无法控制子进程的代码,所以我不能通过在子进程代码中更频繁地刷新来解决这个问题,尽管在每次打印之后刷新似乎对大多数都有帮助(所有?)的问题。

然后你无能为力,因为问题在于子进程。子进程调用fork,为其子进程提供相同的输出流。然后,两个进程都写入相同的输出流,没有任何保护或同步。没有什么可以阻止exerciser父进程和子进程交替或同时写入其流。

与在shell脚本中执行此操作没有什么不同:

echo -n "test" &
echo -n "ing" &

输出可能是&#34;测试&#34;,但它也可能是&#34; ingtest&#34;或&#34; tiensgt&#34;或许多其他事情。

由于exerciser进程执行此操作,因此如果您期望连贯输出,则必须修复exerciser程序。除了不把混乱放入管道之外,没有办法解决从管道出来的混乱。