使用 stdout/stderr 以外的管道与子进程通信

Posted

技术标签:

【中文标题】使用 stdout/stderr 以外的管道与子进程通信【英文标题】:Communicate with a subprocess using pipes on other than stdout/stderr 【发布时间】:2015-02-13 17:46:43 【问题描述】:

这就是我 fork/exec 一个子进程并与之通信的方式(伪代码):

int cout_pipe[2];
pipe(cout_pipe);
fork();
if (child)

  dup2(cout_pipe[1],STDOUT_FILENO);
  execv();

if (parent)

  File* cout_file = fdopen(cout_pipe[0], "r");

我的问题是:

我真的不想读取子进程的标准输出,我希望子进程在标准输出以外的文件描述符上写入。而且,我希望从该文件描述符中读取上述代码。

子进程用python写,主进程用C++写。

我想我可以改变那行,从:

dup2(cout_pipe[1],STDOUT_FILENO);

dup2(cout_pipe[1],MY_OWN_RANDOM_FD_NUMBER);

并以我写入 MY_OWN_RANDOM_FD_NUMBER 而不是标准输出的方式对子进程进行编码。

这是一个好的解决方案吗?可行吗?我怎样才能找出一个好的 MY_OWN_RANDOM_FD_NUMBER?有更好的选择吗?

【问题讨论】:

【参考方案1】:

除非您需要特定的 fd,例如标准输出,否则 dup2 没有任何意义。只需将cout_pipe[1] 转换为字符串,并将其作为命令行参数传递给子进程(在exec 中)。在 Python 端,从sys.argv 中提取参数,将其转换回int,然后使用os.fdopen 为其获取file

【讨论】:

将 cout_pipe[1] 转换为字符串?所以这意味着 pipe(cout_pipe) 使 cout_pipe[1] 成为有效的 fd?。还有什么是转换 itoa(cout_pipe[1]) 的最佳方式? @Kam pipe( cout_pipe ) 将管道的两个 fd 放入 cout_pipe。您可能想要使用dup2 的唯一原因是您对 fd 的数值不满意。至于转换,任何标准解决方案都可以解决问题;如果您在子端执行此操作,就在exec 之前,并且您的系统有itoa(它不是标准的),那么即使这样也是可以接受的;如果您使用的是 C++11,则可以使用 std::to_string——只需确保在调用 .c_str() 之前将结果保存到具有足够生命周期的变量中即可。【参考方案2】:

在您描述的情况下,无需复制文件描述符。对 pipe() 的调用已经在 cout_pipe 数组中创建了您需要的文件描述符。

通常,子进程在执行之前简单地关闭管道的读取端,而父进程关闭管道的写入端。子进程将它想要的任何内容写入写入端,父进程读取或以其他方式监视管道的读取端。

如果您的问题是“子进程如何知道它执行后的 fd 编号,那么我会说当您在子进程中调用 execv() 时将其作为执行参数传递。”

类似:

int   child_pipe[2];
FILE *child_file;

void forkchild(void)

    int  ret;
    char child_pipe_fd_str[16];

    pipe(child_pipe);  /* TODO: check return value! */

    if (0 == (ret = fork())) /* child */
    
        close(child_pipe[0]);
        sprintf(child_pipe_fd_str, "%d", child_pipe[1]);
        execv(my_python_script, child_pipe_fd_str, NULL);
    
    else if (-1 != ret)  /* parent */
    
        close(cout_pipe[1]);
        child_file = fdopen(cout_pipe[0], "r");
    
    else
    
        perror("Fork failed!");
        abort();
    

【讨论】:

在上述情况下,当child写入stdout时,stdout之前被重定向到pipe,因此父进程只需从pipe读取即可读取child的stdout。我希望孩子不要写入标准输出,而是写入另一个文件,并且父进程可以读取它。 如果你想将管道的写入端包装在一个文件中,然后在你执行之后在你的子进程中调用 fdopen(write_side, "w")。诀窍是知道您执行的子 AFTER 中管道写入端的文件描述符#。一种方法是在调用 execv() 时将其作为执行参数显式传递。清楚吗? 好的,所以我应该在 fork 之后,exec 之前创建一个 FILE,然后 dup2(cout_pipe[1],this_files_fd),然后将 FILE 的 fd 作为 exec 的参数传递给子进程,并在 python 中我给这个 fd 写信? 不,您应该使用 os.fdopen 在您的新 Python 进程中创建您执行的文件结构 AFTER。诀窍是知道你需要打开什么fd#。您应该将其作为执行参数传递给子 C 进程中的 execv() 。我会在我的答案中发布一些代码。

以上是关于使用 stdout/stderr 以外的管道与子进程通信的主要内容,如果未能解决你的问题,请参考以下文章

我如何在 .NET 中管道 stdout/stderr?

如何在 Windows 中访问继承的匿名管道句柄,而不是 stdout、stderr 和 stdin?

使用参数 + stdout + stderr 从命令行调用 MFC 应用程序

如何使用独立的 stdout、stderr 和 stdin 分叉一个新进程?

从 git 捕获 STDOUT/STDERR

Monolog stdout/stderr 记录到 Docker 中的当前终端进程