2

环境: x86_64 上的 Linux 2.6.32 (RHEL 6.3) 和 gcc 4.4.6

背景:我正在运行一些繁重的数据处理:~500 GB 输入数据分布在~2000 个文件中。我的主进程分叉了 N 个子进程,每个子进程都会收到一个要处理的文件名列表。

我想要的是控制台 I/O 通过父级。我一直在研究pipe()并看到一些关于使用poll()让我的父母阻止的有趣的东西,直到有错误消息要阅读。看来我需要有 N 个管道(每个孩子一个)并传递poll()有关我想听什么信号的信息。另外,我认为一旦我dup2(pipe[1], STDOUT)在每个孩子中,每个孩子都应该能够cout << stuff;像往常一样向管道写入,对吧?

首先,我上面所说的关于多个管道的内容是poll()正确dup2()的吗?

其次,如何设置父poll()循环,以便在所有孩子都死后继续前进?

现在,这段(不完整的)代码如下:

int status;
while (1) { // wait for stuff
    while ((status = poll(pollfds, ss.max_forks, -1)) > 1)
        cout << "fork "<< status << ": " << pipes[status][0];
    if (status == -1)   Die(errno, "poll error");
    if (status == 0) { // check that we still have at least one open fd
        bool still_running = false;
        for (int i=0; i<ss.max_forks; i++) {
             // check pipe i and set still_running if it is not zero
        }
        if (!still_running)
            break;
    }
}

第三,我应该设置什么,什么时候应该用 fcntl() 设置?我想做 O_ASYNC 吗?我想做阻塞还是非阻塞?

4

2 回答 2

2

实际上,您需要 close() 两个进程(父进程和子进程)中各自的“未使用”端,以确保遇到“断管”。因此,如果子进程写入 Pipe[0],则父进程将从 Pipe[1] 读取并关闭自己的 Pipe[0]。同样,孩子将关闭 Pipe[1]。

如果你这样做,在孩子死后,父母从管道中读取时会出错。不要忘记使用其中一种 waitpid() 风格的函数来清理死进程。

您可能希望将句柄设置为非阻塞,这样您就可以读取其中的任何内容,而不必使用效率极低的 1 字节读取。虽然我只是使用合适的缓冲区大小(通常为 1024 或 4096)调用 read(),但如果有更多数据则让下一次轮询触发。但是,我通常只有一个孩子可以一起工作,而不是几百个:-)

至于您的循环,您必须跟踪每个孩子的状态,并在没有活着的孩子时退出。

编辑:实际上,即使设置了 POLLIN,或者当我得到 POLLERR 或 POLLHUP 标志时,我发现当我读取 0 字节时,我认为孩子已经死了。不知道哪种情况是正确的...

于 2012-09-19T20:03:25.580 回答
1

这里有一个关于select()vs的问题poll()poll 和 select 有什么区别?

但是,您是否考虑过使用线程而不是单独的进程?您可以更好地控制与线程的交互,并且通信是通过数据结构而不是流水线 I/O 流进行的。管道很昂贵;您正在通过操作系统,并且您必须格式化输出结果/解析输入结果,这也很昂贵。

最后一点:无论您使用的是轻量级线程还是重量级 fork/exec,除非您拥有 2000 个 CPU 多处理器,否则您不希望一次启动 2000 个线程。如果争用 CPU 的线程/进程多于 CPU,则会造成代价高昂且持续上下文交换的情况。

于 2012-09-19T21:37:52.030 回答