可能的重复:

在过去的三天里,我一直在尝试使用 dup/dup2 和 fork 来学习 Linux 中的管道。我想我已经掌握了窍门,但是当我从子进程调用两个不同的程序时,似乎我只捕获第一个调用的程序的输出。 我不明白为什么会这样和/或我做错了什么。这是我的首要问题。

编辑:我认为一个可能的解决方案是分叉另一个孩子并使用 dup2 设置管道,但我更想知道为什么下面的代码不起作用。我的意思是,我希望从第一个 execl 调用中捕获 stderr,并从第二个调用中捕获 stdout。这似乎并没有发生。

我的第二个问题是我是否正确打开和关闭管道。如果没有,我想知道我需要添加/删除/更改什么。

这是我的代码:

#include <stdlib.h>
#include <iostream>
#include <time.h>
#include <sys/wait.h>

#define READ_END 0
#define WRITE_END 1

void parentProc(int* stdoutpipe, int* stderrpipe);
void childProc(int* stdoutpipe, int* stderrpipe);

int main(){
    pid_t pid;
    int status;

    int stdoutpipe[2]; // pipe
    int stderrpipe[2]; // pipe

    // create a pipe
    if (pipe(stdoutpipe) || pipe(stderrpipe)){
        std::cerr << "Pipe failed." << std::endl;
        return EXIT_FAILURE;
    }

    // fork a child
    pid = fork();
    if (pid < 0) {
        std::cerr << "Fork failed." << std::endl;
        return EXIT_FAILURE;
    } 
    // child process
    else if (pid == 0){
        childProc(stdoutpipe, stderrpipe);  

    }
    // parent process
    else {
        std::cout<< "waitpid: " << waitpid(pid, &status, 0)
             <<'\n'<<std::endl;
        parentProc(stdoutpipe, stderrpipe);
    }

    return 0;
}



void childProc(int* stdoutpipe, int* stderrpipe){
    dup2(stdoutpipe[WRITE_END], STDOUT_FILENO);
    close(stdoutpipe[READ_END]);

    dup2(stderrpipe[WRITE_END], STDERR_FILENO);
    close(stderrpipe[READ_END]);

    execl("/bin/bash", "/bin/bash", "foo", NULL);
    execl("/bin/ls", "ls", "-1", (char *)0);

    //  execl("/home/me/outerr", "outerr", "-1", (char *)0);

    //char * msg = "Hello from stdout";
    //std::cout << msg;

    //msg = "Hello from stderr!";
    //std::cerr << msg << std::endl;

    // close write end now?
}

void parentProc(int* stdoutpipe, int* stderrpipe){
    close(stdoutpipe[WRITE_END]);
    close(stderrpipe[WRITE_END]);

    char buffer[256];
    char buffer2[256];
    read(stdoutpipe[READ_END], buffer, sizeof(buffer));
    std::cout << "stdout: " << buffer << std::endl;

    read(stderrpipe[READ_END], buffer2, sizeof(buffer));
    std::cout << "stderr: " << buffer2 << std::endl;

    // close read end now?
}

当我运行它时,我得到以下输出:

yfp> g++ selectTest3.cpp; ./a.out 
waitpid: 21423

stdout: hB�(6
stderr: foo: line 1: -bash:: command not found

“outerr”二进制文件(上面注释掉)的源代码很简单:

#include <iostream>

int main(){
    std::cout << "Hello from stdout" << std::endl;
    std::cerr << "Hello from stderr!" << std::endl;
    return 0;
}

当我调用“outerr”而不是 ls 或“foo”时,我得到以下输出,这是我所期望的:

yfp> g++ selectTest3.cpp; ./a.out 
waitpid: 21439

stdout: Hello from stdout

stderr: Hello from stderr!
有帮助吗?

解决方案

execl

一旦您成功调用 execl 或任何其他功能 exec 族,原来的进程被新进程完全覆盖。这意味着新进程永远不会“返回”旧进程。如果你有两个 execl 如果连续调用,则执行第二个调用的唯一方法是第一个调用失败。

为了连续运行两个不同的命令,您必须 fork 一个孩子运行第一个命令, wait, fork 第二个孩子运行第二个命令,然后(可选) wait 对于第二个孩子也是如此。

read

read 系统调用不会附加终止空值,因此通常您需要查看返回值,它告诉您实际读取的字节数。然后将以下字符设置为 null 以获取 C 字符串,或者使用范围构造函数 std::string.

在管道上

现在你正在使用 waitpid 等到子进程完成, 然后 从管道中读取。这样做的问题是,如果子进程产生大量输出,那么它将 堵塞 因为管道已满并且父进程没有从中读取数据。结果将是死锁,因为子进程等待父进程读取,而父进程等待子进程终止。

你应该做的是使用 select 等待输入到达孩子的 stdout 或孩子的 stderr. 。当输入到达时,读取它;这将使孩子能够继续下去。当子进程终止时,您就会知道,因为您将得到两个文件的结尾。 然后 你可以放心地打电话 wait 或者 waitpid.

其他提示

exec族族具有新的过程图像的当前过程映像。当您执行时,

execl("/bin/bash", "/bin/bash", "foo", NULL);
.

来自当前进程的代码不再执行。这就是为什么你从未看到执行的结果

execl("/bin/ls", "ls", "-1", (char *)0);
.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top