在 OSX 上创建子进程并将子进程的标准输入和标准输出重定向到父进程?

标签 c macos exec fork pipe

我正在 OSX 上的 Xcode 中开发 C 程序。

(父)程序必须启动一个新的(子)进程,该进程通过标准输入接收输入并将结果输出到标准输出。所以父进程将数据写入子进程的标准输入,父进程从子进程的标准输出读取结果。

在 Windows 上,我使用 CreateProcess 执行上述操作,但我不确定在 C 中的 OSX 上是如何完成的。

我相信我应该使用 exec 来启动进程,但我看不到如何重定向 exec 启动的可执行文件(子进程)的标准输入和标准输出。从阅读手册来看,如果我使用 exec,子进程似乎也会成为父进程。子进程和父进程必须并行运行,以便父进程可以在需要时写入和读取子进程。

有好心的 OSX C 专家可以给我一个简短的例子来说明上面是如何完成的吗?

谢谢

编辑

我想我明白了。但是,如果子进程是一个无限的 while 循环,等待 stdin 的输入,那么它就不会变成“僵尸”,对吧?

子进程基本上是这样做的:

1. Read data from stdin (i.e. blocked until data is received)
2. Process data
3. Write result to stdout
4. Goto 1

看完你的帖子后,我找到了这个页面:

http://www.jukie.net/~bart/snippets/popenRWE/popenRWE.c.html

但是,我在启动 .exe(子进程)时遇到了问题

在终端中,我会像这样启动 .exe:

./myApp.exe someParam1 someParam2 someParam3

API 看起来像这样:

popenRWE(int *rwepipe, const char *exe, const char *const argv[])

我猜第二个参数应该是:

 const char* exe = "./myApp.exe";

第三个参数应该是:

 char* p0 = "./myApp.exe";
 char* p1 = "someParam1";
 char* p2 = "someParam2";
 char* p3 = "someParam3";

 char** argv[4] = {p0, p1,p2,p3};

我说得对吗?

最佳答案

我包含了我不久前编写的一个小型库的源代码。那应该让你开始。 Fork/pipe/exec 并不是那么容易(尤其是 exec 的所有变体),我也花了一段时间。所以这里是:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include "limbo.h"

int out_pipe[2], err_pipe[2];

int run_command(char *argv[], int *out_length, int *err_length){
    pid_t pid;
    int status = 0;
    struct stat out_stat, err_stat;

    pipe(out_pipe); //create a pipe
    pipe(err_pipe);
    if(!(pid = fork())) //spawn child 
    {
        // Child. Close the read end of the pipe
        close(out_pipe[0]);
        close(err_pipe[0]);
        // redirect stdout and stderr to the write end of the pipe
        dup2(out_pipe[1], STDOUT_FILENO);
        dup2(err_pipe[1], STDERR_FILENO);
        status = execv(argv[0], argv); //child will terminate here
    }

    //Only parent gets here. Close write end of the pipe
    close(out_pipe[1]);
    close(err_pipe[1]);
    //or wait for the child process to terminate
    waitpid(pid, &status, 0);

    fstat(out_pipe[0], &out_stat);
    fstat(err_pipe[0], &err_stat);
    
    *out_length = (int) out_stat.st_size;
    *err_length = (int) err_stat.st_size;
    
    return status;
}

int read_buffers(char *out_buffer, int out_length, char *err_buffer, int err_length){
    out_buffer[read(out_pipe[0], out_buffer, out_length)] = 0;
    err_buffer[read(err_pipe[0], err_buffer, err_length)] = 0;
    
    return 0;
}

代码中的注释应该有助于您理解代码。请随意重复使用。

编辑

回应您的评论:

waitpid() 调用使父进程等待子进程的终止。如果你想让两个进程并行运行,你需要在我使用它的地方去掉 waitpid()但要小心:如果不调用其中一个 wait 函数,您的子进程将在完成后变成僵尸进程。您有责任关注您的子进程并等待,以便内核清理该进程。

关于在 OSX 上创建子进程并将子进程的标准输入和标准输出重定向到父进程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19233767/

相关文章:

c - 在字符后读取多个数字

c++ - 编译 mpeg4ip 会出现解引用类型双关指针错误

macos - 如何在 OS X 中卸载 SBT?

git -/.git/hooks/: No such file or directory protocol error: expected control record on Mac osx

regex - 正则表达式可以用于 Fluid 应用程序中的 URL 模式吗?

c - 像下面这样使用 exec() 的函数的返回值是多少?

c - 使用堆栈的中缀到后缀的转换

c - C 中的 0/0 - gcc-7 或更高版本

windows - 与 bash 的 exec() 等效的 PowerShell 是什么?

php - 在 PHP 中执行 perl