我最近需要编写一个执行 os.fork()的脚本来分成两个进程。子进程成为服务器进程,并使用使用 os.pipe()创建的管道将数据传递回父进程。孩子关闭管道的'r'
端,父母像往常一样关闭管道的'w'
端。我使用 os.fdopen 将pipe()的返回值转换为文件对象。
我遇到的问题是:流程成功分叉,孩子成为服务器。一切都很好,孩子尽职尽责地将数据写入管道的开放'w'
端。不幸的是,管道的父端做了两件奇怪的事情:
A)它阻塞了管道read()
端的'r'
操作
其次,除非'w'
结束完全关闭,否则它无法读取放在管道上的任何数据。
我立即认为缓冲是问题,并添加了 pipe.flush()调用,但这些没有帮助。
有人可以解释为什么数据在写入结束完全关闭之前不出现的原因吗?是否有策略使read()
调用非阻塞?
这是我的第一个分叉或使用管道的Python程序,如果我犯了一个简单的错误,请原谅我。
答案 0 :(得分:12)
您是在使用read()而未指定大小,还是将管道视为迭代器(for line in f
)?如果是这样,那可能是你的问题的根源 - read()被定义为在返回之前读取文件的末尾,而不是只读取可用于读取的内容。这意味着它会阻塞,直到孩子调用close()。
在链接到的示例代码中,这没关系 - 父代表以阻塞方式执行,只是将子代码用于隔离目的。如果你想继续,那么要么使用非阻塞IO,就像你发布的代码一样(但是要准备处理半完成数据),或者读取块(例如r.read(size)或r.readline()) )只会在读取特定大小/行之前阻止。 (你仍然需要给孩子打电话)
看起来将管道视为迭代器也使用了一些其他缓冲区,因为“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)
答案 1 :(得分:5)
使用
fcntl.fcntl(readPipe, fcntl.F_SETFL, os.O_NONBLOCK)
在调用read()之前解决了这两个问题。 read()调用不再阻塞,并且数据仅在写入端的flush()之后出现。
答案 2 :(得分:4)
我发现你已经解决了阻止i / o和缓冲的问题。
如果您决定尝试不同的方法,请注意:subprocess是fork / exec习惯用语的等效/替换。看起来这不是你正在做的事情:你只有一个fork(不是exec)并在两个进程之间交换数据 - 在这种情况下multiprocessing
模块(在Python 2.6+中)会更好适合。
答案 3 :(得分:-9)
Python应用程序中fork的“父”与“子”部分是愚蠢的。这是16位unix时代的遗产。从fork / exec和exec是重要事物的那一天开始,这是一种做法,可以充分利用一个小小的处理器。
将Python代码分成两个独立的部分:父级和子级。
父部分应使用subprocess来运行子部分。
fork和exec可能发生在那里的某个地方 - 但你不需要关心。