您可能使用了错误的函数 - 当您希望调用程序写入分叉进程的标准输入或从其标准输出读取时,使用了popen()。看来你两个都不想要。它还需要两个参数。
你的要求也有些矛盾:
我希望它(理想情况下)从父级继承标准输入和标准输出
对父级的任何输入都会传给子级,子级的任何输出都会返回给父级
但至少,我希望它继承标准输入并将标准输出写入命名管道
第一个选项很简单 - 它不需要特殊编码。提供给父级标准输入的任何数据也将在子级标准输入上可用(但只有两个进程中的一个可以读取它)。孩子的标准输出通常会和父母的标准输出放在同一个地方。如果您希望父母阅读孩子的标准输出,那么您确实需要一个管道 - 然后popen() 是合适的,但“至少”的东西令人困惑。
那么,让我们定义一下你真正想要什么?
选项 1
- 子级的标准错误应该转到命名管道。
- 调用进程应读取子级的标准输出。
- 子项的标准输入应与父项的标准输入来自同一位置。
- 命名管道已存在。
因此:
FILE *fp = popen("/run/my/command -with arguments 2>/my/other/pipe", "r");
请注意,孩子将被挂起,直到一个进程打开“/my/other/pipe”进行阅读;这反过来意味着如果父进程从 fp 读取,它也将被挂起,直到其他进程打开 '/my/other/pipe' 进行读取。
选项 2
- 子级的标准错误应该转到命名管道。
- 子级的标准输出应该转到父级的标准输出。
- 子项的标准输入应与父项的标准输入来自同一位置。
- 命名管道已存在。
现在popen() 不合适,我们进入了赤裸裸的“fork & exec”代码。以下是比可操作的 C 更多的伪代码。
if ((pid = fork() < 0)
error
else if (pid > 0)
{
/* Parent - might wait for child to complete */
}
else
{
int fd = open("/my/other/pipe", O_WRONLY|O_NONBLOCK);
if (fd < 0)
error
dup2(fd, 2); /* There is a symbolic name for stderr too */
close(fd); /* Do not want this open any more */
char *cmd[4] = { "/bin/sh", "-c", "/run/my/command -with arguments", 0 };
execv(cmd[0], cmd);
error - if execv returns, it failed!
}
如果您完全确信没有人像关闭标准输出那样对您进行任何特技,您可以通过在调用 open() 之前关闭 stderr (fd = 2) 来避免使用 dup2()。但是,如果您这样做,您将无法再报告任何错误 - 因为您关闭了 stderr。所以,我会按照图示来做。
如果您有不同的要求,请说明您想要实现的目标。
正如 p2vb 所指出的,如果您希望父母等待孩子完成,那么只需使用 system() 就足够了。如果父进程在子进程运行时应该继续,您可以尝试system(),其中命令字符串以与符号 (&) 结尾以将子进程置于后台,或者您可以使用上面选项 2 中概述的代码。
使用system(),父级将几乎没有机会读取从子级获取标准错误的/my/other/pipe。如果孩子生产很多,你很容易陷入僵局。
此外,请注意您的 FD_CLOEXEC 标志 - 将其设置在您不希望孩子修改的文件上。在 Linux 上,您可以在 open() 调用上使用 O_CLOEXEC 标志;使用 Solaris,您必须通过 fcntl() 进行设置 - 小心。