管道、分叉和​​非阻塞 IPC

Posted

技术标签:

【中文标题】管道、分叉和​​非阻塞 IPC【英文标题】:pipe, fork, and non-blocking IPC 【发布时间】:2011-07-08 05:57:36 【问题描述】:

所以我试图在 fork() 之后在子进程中运行系统命令(或 exec 或其他),并将一些输入推送给它,然后获取它的输出。在fork()之后是这样的,pc和cp是父子管道和子父管道。

  case 0:
        /* Child. */
        close(STDOUT_FILENO); /* Close current stdout. */
        dup2(cp[1], STDOUT_FILENO);

        close(STDIN_FILENO);
        dup2(pc[0], STDIN_FILENO);

        close( pc[1]);
        close( cp[0]);
        execlp("cat", "cat", NULL);
        exit(1);
  default:
        /* Parent. */
        /* Close what we don't need. */
        printf("Input to child:\n");

        string theinput("Hey there baby");
        write(pc[1], theinput.c_str(), theinput.size());
        close(pc[1]);

        cout << "The input : " << theinput << endl;


        printf("\nOutput from child:\n");
        close(cp[1]);
        while( read(cp[0], &ch, 1) == 1)
        
           write(1, &ch, 1);
           outcount++;
        

        exit(0);

现在,它似乎工作得很好(如果你想要代码:http://pastebin.com/Fh7GrxYm),但是当我在 irc 上的 #posix 上讲话时,他们疯了,关于这可能如何阻止,以及它如何“取决于关于内核的感受”。

有一篇关于同一件事的 msdn 博客文章:http://blogs.msdn.com/b/oldnewthing/archive/2011/07/07/10183884.aspx

如何防止阻塞(如果有的话)等?

【问题讨论】:

【参考方案1】:

在你的情况下,当父进程到达这一行时,可能会发生死锁:

write(pc[1], theinput.c_str(), theinput.size());

如果“输入”是很多数据,那么父进程可能会填满pc 管道。子进程(此处为cat)可能会读取其中的一部分但不是全部。 cat 他们会将其回馈给您。但同样,如果它有很多数据,它可能会填满cp 管道并阻塞,直到有人从该管道中读取数据。这永远不会发生,因为父进程被阻塞等待pc 管道耗尽,并且永远不会到达消耗cp 管道内容的代码。死锁。

就像你的 IRC 伙伴说的那样,这是否发生取决于很多事情,例如所涉及的数据量、管道在阻塞之前可以容纳的数据量(依赖于内核的参数)、 stdio 或其他由父进程或子进程执行的缓冲等...

您的选择是:

    使用两个进程来控制外部命令:一个向其提供数据,另一个将结果读回。为此,您必须两次fork()。这看起来很像一个 shell 管道。按照惯例,最终数据源是孙进程,过滤器是中间父进程,最终数据接收器是授权父进程。

    使用两个线程来控制外部命令。与上一个选项类似。

    使用非阻塞 I/O 来控制外部命令。使用fcntl() 将两个文件描述符设置为非阻塞模式,并使用poll()select() 设置一个事件循环以等待任一文件描述符准备好。当任一文件描述符准备就绪时,请准备好让write() 仅完成部分操作,并让read() 不会一次读取所有内容。

    使用现有的事件循环,如glib's,将管道设置为IO Channels,并设置watch them 以了解何时读取或写入数据。与上一个选项类似,但使用现有框架,因此您可以与现有应用程序事件循环集成。

顺便说一句:您的 exit(1) 应该是 _exit(1) 以防止 C 库在短暂的子进程中不恰当地调用退出时间挂钩。

【讨论】:

以上是关于管道、分叉和​​非阻塞 IPC的主要内容,如果未能解决你的问题,请参考以下文章

未发出信号的非阻塞 ConnectNamedPipe 事件

Linux有名管道的 阻塞VS非阻塞 读写

非阻塞命名管道

python非阻塞recv与进程之间的管道?

PHP中管道的非阻塞打开

NIO--05--非阻塞网络通信 选择器(Selector)和 管道(Pipe)