13

我最近需要编写一个脚本来执行os.fork()以拆分为两个进程。子进程成为服务器进程并使用使用os.pipe()创建的管道将数据传递回父进程。像往常一样,孩子关闭'r'管道的末端,父母关闭管道的'w'末端。我使用os.fdopen将 pipe() 的返回转换为文件对象。

我遇到的问题是:进程成功分叉,孩子成为服务器。一切正常,孩子尽职尽责地将数据写入'w'管道的开放端。不幸的是,管道的父端做了两件奇怪的事情:
A)它阻塞了管道末端的read()操作'r'。其次,除非末端完全关闭
,否则它无法读取放在管道上的任何数据。'w'

我立即认为缓冲是问题并添加了pipe.flush()调用,但这些没有帮助。

谁能解释为什么在写入端完全关闭之前数据不会出现?是否有使read()呼叫非阻塞的策略?

这是我第一个分叉或使用管道的 Python 程序,如果我犯了一个简单的错误,请原谅我。

4

4 回答 4

13

您是在使用 read() 时未指定大小,还是将管道视为迭代器 ( for line in f)?如果是这样,那可能是您的问题的根源- read() 被定义为在返回之前读取到文件末尾,而不仅仅是读取可读取的内容。这意味着它将阻塞,直到孩子调用 close()。

在链接到的示例代码中,这是可以的 - 父级以阻塞方式行事,并且仅将子级用于隔离目的。如果您想继续,请使用您发布的代码中的非阻塞 IO(但要准备处理半完整的数据),或者分块读取(例如 r.read(size) 或 r.readline() ) 这只会阻塞直到读取了特定的大小/行。(你仍然需要在孩子身上调用flush)

看起来将管道视为迭代器也在使用一些进一步的缓冲区,因为for line in r:如果您需要立即使用每一行,“”可能不会给您想要的东西。可以禁用此功能,但仅在 fdopen 中为缓冲区大小指定 0 似乎还不够。

下面是一些应该工作的示例代码:

import os, sys, time

r,w=os.pipe()
r,w=os.fdopen(r,'r',0), os.fdopen(w,'w',0)

pid = os.fork()
if pid:          # Parent
    w.close()
    while 1:
        data=r.readline()
        if not data: break
        print "parent read: " + data.strip()
else:           # Child
    r.close()
    for i in range(10):
        print >>w, "line %s" % i
        w.flush()
        time.sleep(1)
于 2009-05-16T15:30:19.490 回答
6

使用

fcntl.fcntl(readPipe, fcntl.F_SETFL, os.O_NONBLOCK)

在调用 read() 之前解决了这两个问题。read() 调用不再阻塞,数据仅在写入端的 flush() 之后出现。

于 2009-05-16T02:14:38.577 回答
5

我看到你已经解决了阻塞 i/o 和缓冲的问题。

如果您决定尝试不同的方法,请注意: subprocess 是 fork/exec 习语的等价物/替代品。看起来这不是您正在做的事情:您只有一个 fork(不是 exec)并在两个进程之间交换数据——在这种情况下,multiprocessing模块(在 Python 2.6+ 中)会更合适。

于 2009-05-16T02:56:06.683 回答
-10

Python 应用程序中 fork 的“父”与“子”部分是愚蠢的。这是 16 位 unix 时代的遗产。当 fork/exec 和 exec 是充分利用一个很小的处理器的重要事情时,这是一种矫揉造作。

将您的 Python 代码分成两个独立的部分:父级和子级。

父部件应该使用子进程来运行子部件。

一个 fork 和 exec 可能会发生在那里的某个地方——但你不需要关心。

于 2009-05-16T02:05:01.860 回答