我有一个小C服务器需要接受连接并分叉子进程。我需要将子进程的stderr
转到已存在的命名管道,将stdout
转到父级的stdout
,然后stdin
孩子tp的来源与父母的stdin
相同。
我最初的尝试涉及popen()
,但我似乎永远无法得到我想要的东西。
最后,这个特定的解决方案只需要在Solaris中运行。感谢。
编辑:更新了问题,希望能更准确地描绘我想要完成的任务。谢谢你对我有耐心。
EDIT2:我还需要父进程来获取子进程的返回值,然后对它做一些事情,如果这有任何区别。
答案 0 :(得分:5)
您可能使用了错误的函数 - 当您希望调用程序写入分叉进程的标准输入或从其标准输出读取时,使用popen()
。看来你也不想要。它还需要两个参数。
您的要求也有些矛盾:
我希望它(理想情况下)从父母继承stdin和stdout
父母的任何输入都会转到子级,而子级的任何输出都会返回到父级
但至少,我希望它继承stdin并将stdout写入命名管道
第一个选项很简单 - 它不需要特殊编码。提供给父级的stdin的任何数据也将在子级的stdin上可用(但只有两个进程中的一个将读取它)。孩子的stdout通常会和父母的stdout去同一个地方。如果你想让父母读取孩子的标准输出,那么你确实需要一个管道 - 然后popen()
是合适的,但是“至少”的东西是令人困惑的。
那么,让我们定义你真正想要的东西吗?
因此:
FILE *fp = popen("/run/my/command -with arguments 2>/my/other/pipe", "r");
请注意,子进程将挂起,直到进程打开'/ my / other / pipe'进行读取;这反过来意味着如果父进程从fp
读取,它也将被挂起,直到其他进程打开'/ my / other / pipe'进行读取。
现在popen()
不合适,我们进入赤裸的`fork&执行代码。接下来是比操作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!
}
如果你完全有信心没有人像你关闭stdout那样对你施加任何特技,你可以通过在调用dup2()
之前关闭stderr(fd = 2)来避免使用open()
。但是,如果您这样做,则无法再报告任何错误 - 因为您关闭了stderr。所以,我会这样做。
如果您有不同的要求,请说明您想要达到的目标。
如p2vb所述,如果您希望父母等待孩子完成,那么仅使用system()
就足够了。如果父级应该在子级运行时继续,则可以尝试system()
,其中命令字符串以&符号(&
)结束以将子项置于后台,或者您可以使用上面的备选方案2。
使用system()
,父母几乎没有机会阅读/ my / other / pipe,它会从孩子那里获得标准错误。如果孩子经常生产,你很容易陷入僵局。
另外,请注意FD_CLOEXEC标志 - 将其设置在您不希望子项修改的文件上。在Linux上,您可以在open()
调用上使用O_CLOEXEC标志;对于Solaris,您必须通过fcntl()
- 仔细设置它。