Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/57.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 在OSX上创建子进程并将子进程的stdin和stdout重定向到父进程?_C_Macos_Exec_Fork_Pipe - Fatal编程技术网

C 在OSX上创建子进程并将子进程的stdin和stdout重定向到父进程?

C 在OSX上创建子进程并将子进程的stdin和stdout重定向到父进程?,c,macos,exec,fork,pipe,C,Macos,Exec,Fork,Pipe,我正在OSX上用Xcode编写一个C程序 (父)程序必须启动一个新的(子)进程,该进程通过stdin接收输入,并将结果输出到stdout。因此,父进程将数据写入子进程的stdin,父进程从子进程的stdout读取结果 在Windows上,我使用CreateProcess来完成上述操作,但我不确定在C中的OSX上是如何完成的 我相信我应该使用exec来启动进程,但我不知道如何重定向exec启动的可执行文件(子进程)的stdin和stdout。从阅读手册来看,如果我使用exec,子进程也会变成父进程

我正在OSX上用Xcode编写一个C程序

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

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

我相信我应该使用exec来启动进程,但我不知道如何重定向exec启动的可执行文件(子进程)的stdin和stdout。从阅读手册来看,如果我使用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
在我阅读了你的帖子后,我发现了这个页面:

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

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

popenRWE(int *rwepipe, const char *exe, const char *const argv[])
./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};
第三个论点应该是:

 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;
}
#包括
#包括
#包括
#包括
#包括“limbo.h”
int out_管道[2],err_管道[2];
int run_命令(char*argv[],int*out_长度,int*err_长度){
pid_t pid;
int status=0;
结构统计输出统计,错误统计;
管道(out_pipe);//创建管道
管道(错误管道);
if(!(pid=fork())//生成子对象
{
//孩子。关闭管道的读取端
关闭(输出管道[0]);
关闭(错误管道[0]);
//将stdout和stderr重定向到管道的写入端
dup2(输出管道[1],标准文件号);
dup2(错误管道[1],标准文件号);
status=execv(argv[0],argv);//子级将在此处终止
}
//只有父级到达此处。请关闭管道的写入端
关闭(管道[1]);
关闭(错误管道[1]);
//或者等待子进程终止
waitpid(pid和status,0);
fstat(输出管道[0],&out\u stat);
fstat(err_管道[0],&err_stat);
*out_length=(int)out_stat.st_size;
*err_length=(int)err_stat.st_size;
返回状态;
}
整数读取缓冲区(字符*输出缓冲区,整数输出长度,字符*错误缓冲区,整数错误长度){
out_buffer[读取(out_管道[0],out_buffer,out_长度)]=0;
err_buffer[读取(err_管道[0],err_buffer,err_长度)]=0;
返回0;
}
代码中的注释应有助于您理解代码。请随意重复使用

编辑 针对你的评论:


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

如果我错了,请纠正我……但是,第二次查看代码时,父进程似乎不会与子进程并行运行??看起来父进程正在等待子进程终止。在我的例子中,子进程和父进程必须并行运行。@user1884325我在回答中包含了您评论中的问题答案。1。只要你的孩子没有终止,那么它就不会变成僵尸,这是正确的。但是:当父进程终止时,您必须清理(即,
等待
)您的子进程。否则,子进程将成为启动进程的子进程,您将无法终止它(以一种很好的方式)。这些参数对我来说是正确的(尽管我不熟悉Windows的细节。在Unix上应该可以工作)。不过有一件事:启动看起来更像这样:
/myApp.exe someParam1
(否则您将直接运行exe)。您能澄清一下吗?你说的./myApp.exe someParam1是什么意思?你是说变量exe、p0、p1、p2和p3的设置不正确吗?如果是这样的话,它们应该是什么样子呢?以下是我理解的:您的程序
myApp.exe
是您希望作为子进程运行的程序,它将侦听输入。如果这是真的,那么运行
/myApp.exe someParam1
将直接作为shell的子进程启动
myApp.exe
,实现pipe/fork/exec的整个过程变得毫无意义。从您提供的源代码中,我收集到第二个程序,我们称之为
,它实现了
popenRWE()
,并将应用程序的名称作为参数。要真正使用第二个程序,你必须运行
/myApp.exe
Ahhh…明白了…除非它是通过编程完成的。主程序不使用来自主程序的输入参数。谢谢你的反馈。这非常有用,我真的很感激。