管道,分叉和非阻塞IPC

时间:2011-07-08 05:57:37

标签: posix fork pipe interprocess

所以我试图在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 个答案:

答案 0 :(得分:1)

在您的情况下,父进程到达此行时可能会发生死锁:

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

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

就像你的IRC好友说的那样,这种情况是否发生取决于许多因素,例如所涉及的数据量,管道在阻塞之前可以容纳的数据量(与内核相关的参数),由父母或子女进程执行的stdio或其他缓冲......

您的选择是:

  1. 使用两个进程来控制外部命令:一个用于提供数据,另一个用于读取结果。你必须为此fork()两次。这看起来很像shell管道。通常,最终数据源是孙子进程,过滤器是中间父进程,以及grantparent进程的最终数据接收器。

  2. 使用两个线程来控制外部命令。与之前的选项类似。

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

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

  5. BTW:您的exit(1)应为_exit(1),以防止C库在短期子进程中不恰当地调用退出时挂钩。