【问题标题】:Need help with named pipes and popen需要有关命名管道和 popen 的帮助
【发布时间】:2010-07-27 17:41:34
【问题描述】:

我有一个小型 C 服务器,它需要接受一个连接并派生一个子进程。我需要子进程的stderr 去一个已经存在的命名管道,子进程的stdout 去父进程的stdout,子进程的stdin 来自同一个放置为父级的stdin

我最初的尝试涉及popen(),但我似乎永远无法得到我想要的。

最后,这个特定的解决方案只需要在 Solaris 中工作。谢谢。

编辑:更新了问题,希望更准确地描述我想要完成的事情。感谢您对我的耐心。

EDIT2:我还需要父进程获取子进程的返回值,然后对它进行处理,如果这有什么不同的话。

【问题讨论】:

  • 你能澄清一下你的问题吗?您说您希望孩子“继承”标准输入和标准输出(即具有与父母相同的标准输入和标准输出)。当你 fork() 没有做任何事情时,这已经发生了 - 然后你还说你希望孩子的输出进入父母的标准输入。您能否将您的问题重新表述得更清楚一点,以免您自相矛盾?
  • 不确定如何用不同的方式表达它;我包括了那个括号,即试图解释我希望它如何表现。我希望父母的标准输入成为孩子的标准输入,孩子的标准输出成为父母的标准输出。随着孩子的标准错误进入命名管道。

标签: c pipe popen


【解决方案1】:

您可能使用了错误的函数 - 当您希望调用程序写入分叉进程的标准输入或从其标准输出读取时,使用了popen()。看来你两个都不想要。它还需要两个参数。

你的要求也有些矛盾:

  • 我希望它(理想情况下)从父级继承标准输入和标准输出

  • 对父级的任何输入都会传给子级,子级的任何输出都会返回给父级

  • 但至少,我希望它继承标准输入并将标准输出写入命名管道

第一个选项很简单 - 它不需要特殊编码。提供给父级标准输入的任何数据也将在子级标准输入上可用(但只有两个进程中的一个可以读取它)。孩子的标准输出通常会和父母的标准输出放在同一个地方。如果您希望父母阅读孩子的标准输出,那么您确实需要一个管道 - 然后popen() 是合适的,但“至少”的东西令人困惑。

那么,让我们定义一下你真正想要什么?

选项 1

  1. 子级的标准错误应该转到命名管道。
  2. 调用进程应读取子级的标准输出。
  3. 子项的标准输入应与父项的标准输入来自同一位置。
  4. 命名管道已存在。

因此:

FILE *fp = popen("/run/my/command -with arguments 2>/my/other/pipe", "r");

请注意,孩子将被挂起,直到一个进程打开“/my/other/pipe”进行阅读;这反过来意味着如果父进程从 fp 读取,它也将被挂起,直到其他进程打开 '/my/other/pipe' 进行读取。

选项 2

  1. 子级的标准错误应该转到命名管道。
  2. 子级的标准输出应该转到父级的标准输出。
  3. 子项的标准输入应与父项的标准输入来自同一位置。
  4. 命名管道已存在。

现在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(),其中命令字符串以与符号 (&amp;) 结尾以将子进程置于后台,或者您可以使用上面选项 2 中概述的代码。

使用system(),父级将几乎没有机会读取从子级获取标准错误的/my/other/pipe。如果孩子生产很多,你很容易陷入僵局。

此外,请注意您的 FD_CLOEXEC 标志 - 将其设置在您不希望孩子修改的文件上。在 Linux 上,您可以在 open() 调用上使用 O_CLOEXEC 标志;使用 Solaris,您必须通过 fcntl() 进行设置 - 小心。

【讨论】:

  • 选项 2 正是我想要实现的。我很抱歉没有在我的问题中更清楚地说明这一点;我在这方面的知识充其量是模糊的,所以我很难准确地表达我想要完成的事情。
  • 如果这是您想要做的,另一种选择是只 fork() 然后 system(),使用 shell 重定向功能将 stderr 重定向到您的命名管道。
  • 这里有很多很棒的信息,我学到了很多。感谢大家的帮助。
猜你喜欢
  • 1970-01-01
  • 2015-04-12
  • 2018-06-12
  • 2019-05-17
  • 2021-02-04
  • 2012-10-22
  • 2015-06-23
  • 2014-12-26
  • 2013-08-12
相关资源
最近更新 更多