【问题标题】:(c/c++) trying to force EOF from parent process sending input to child process(c/c++) 试图强制从父进程向子进程发送输入的 EOF
【发布时间】:2011-04-24 00:58:18
【问题描述】:

我有一个非常简单的 c/c++ 程序,它派生一个子进程来执行另一个程序,然后向该子程序发送一些数据,并等待响应。

子程序从标准输入读取并在继续之前等待 EOF。

我的问题是,子程序接收来自管道写入的初始输入,但它从未看到 EOF(即使我关闭了管道),所以它永远等待。

我不确定为什么关闭管道并不意味着孩子的标准输入的 EOF?

代码如下:

http://gist.github.com/621210

【问题讨论】:

  • 我更新了我的答案。当我考虑这个问题时,这应该有“作业”标签吗?看起来很基础。
  • 我向你保证,这与家庭作业无关。我已经失学多年了。我不是 c/c++ 编码器,但我正在为我正在做的演示构建一个概念验证系统,并且我已经回到了 10 多年没有写过的代码。

标签: c++ c io fork pipe


【解决方案1】:

最常见的原因是您没有关闭管道的写入端,因此 EOF 永远不会被发送。常见的示例是当您的代码如下所示:

int fds[2];
pipe(fds);  // open a pipe
if (fork()) {
    // parent process
    write(fds[1], ...  // write data
    close(fds[1]); // close it
} else {
    // child process
    while (read(fds[0], ....) > 0) {
        // read until EOF

这里的问题是管道的写端永远不会关闭——父进程关闭它,但子进程仍然打开写描述符。所以孩子永远不会在读取描述符上看到 EOF。

在 fork 子节点后,您需要做的第一件事是 close(fds[1]);,关闭其写入描述符的副本。这样,当父级关闭对管道写入端的最后一个剩余引用时,子级将在读取端看到 EOF。

编辑

查看您添加的链接,这正是问题所在 - 子项在其标准输出上仍然打开了管道的写入端。不要将写入端复制到孩子的标准输出,只需将其关闭即可。将标准输出发送到其他地方(日志文件或 /dev/null)

编辑

对于双向通信,您需要两个管道:

int tochild[2], fromchild[2];
pipe(tochild); pipe(fromchild);
if (fork()) {
    close(tochild[0]);
    close(fromchild[1]);
    //write to tochild[1] and read from fromchild[0]
} else {
    dup2(tochild[0], 0);
    dup2(fromchild[1], 1);
    close(tochild[0]); close(tochild[1]);
    close(fromchild[0]); close(fromchild[1]);
    exec(...
}

但是,您需要非常小心地在父级中写入数据——如果要写入大量数据 发送给孩子,您不能在读取孩子的输出之前发送所有内容,否则您可能会死锁(两个管道都已填满,父块试图为孩子写更多数据,而孩子块试图输出)。您需要使用 poll 或 select 来判断何时有数据要读取或有空间要写入,并且您可能希望将管道(至少父级结束)置于非阻塞模式。

【讨论】:

  • 请参阅链接代码要点中的第 32-37 行。我正在关闭它们。
  • 第 33 行将其复制到标准输出,然后永远不会关闭
  • 如果我需要孩子在完成后能够回复父母,如何关闭孩子的实际stdout!?我需要一个双向沟通渠道。我需要给孩子发送数据,我需要看看孩子的反应是什么。
  • 顺便说一句,我没有对子可执行文件的一般控制权。它只是一个读取输入、执行某些操作并以输出响应的程序。
  • @eonil:是的,准确地说。仅当引用计数降至 0 时,底层对象才会关闭(并将 EOF 传播到管道的另一端)
【解决方案2】:

更新了我认为的问题: 您正在读入一个字符并检查该字符的 EOF。这不是 read() 系统调用的工作方式。在 EOF 时它将返回 0。它不会将 EOF 写入缓冲区。

我还看到你一次读一个字符。这是一种糟糕的数据读取方式。它比读取大缓冲区(例如 4 或 8 kB)慢几 倍。

我相信你在这里也有一个常见的错误。你没有检查 write() 的返回值。

write 系统调用不保证在返回前写入所有数据。它可能会写入 4000 个字节并返回。它将返回写入的字节数。然后,您有责任更新缓冲区指针并再次调用 write。

或者它可能会返回一个错误代码,对你来说检查是很重要的。

【讨论】:

  • 很公平。这是简单的概念验证代码。在我的测试中,我发送 5 个字符作为输入。所以我想如果 write() 只发送了其中的一半,我会感到惊讶。
  • 此外,如果我将子程序更改为仅读取 5 个字符(而不是等待 EOF),则子程序会很好地获取所有 5 个字符并按预期工作。所以似乎唯一的问题是我需要孩子等待可变长度输入,而 EOF 似乎是发出这种信号的正确方法。
  • 您关注的是问题的错误部分。我没有问题将孩子的输出返回给父母。当父母将一些数据写入连接到孩子的标准输入的管道然后在写入后关闭管道时,我遇到了孩子没有得到 EOF 的问题。
  • 参见 L44 和 L46... L44 将数据写入管道,该管道应发送到子标准输入。 L44 工作正常。 L46 在 write() 完成后关闭管道。据我之前所知,关闭管道应该将 EOF 沿管道发送到孩子的标准输入。但事实并非如此。
  • @Kyle:我更新了我的答案。第一部分现在添加您的 read() 调用,我认为这是错误的。
【解决方案3】:

这是我为你写的概念证明:

Fork exec double pipe by dino ciuffetti

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-08
    • 1970-01-01
    • 2012-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多