使用os.pipe和os.fork()的Python程序问题
最近我需要写一个脚本,使用os.fork()来分裂成两个进程。子进程变成一个服务器进程,通过一个用os.pipe()创建的管道把数据传回父进程。子进程关闭管道的'r'
端,父进程关闭'w'
端,跟平常一样。我把管道的返回值转换成文件对象,用os.fdopen。
我遇到的问题是这样的:进程成功分裂,子进程变成服务器,一切运行得很好,子进程也按规矩把数据写入管道的'w'
端。可惜的是,父进程的管道端出现了两个奇怪的情况:
A) 在管道的'r'
端进行read()
操作时,它会被阻塞。
其次,父进程无法读取任何放入管道的数据,除非'w'
端完全关闭。
我立刻想到可能是缓冲的问题,于是添加了pipe.flush()的调用,但这些并没有帮助。
有没有人能解释一下为什么数据在写入端完全关闭之前不会出现?还有没有什么方法可以让read()
调用不被阻塞?
这是我写的第一个使用分叉和管道的Python程序,如果我犯了简单的错误,请多多包涵。
4 个回答
我看到你已经解决了阻塞输入输出和缓冲的问题。
如果你决定尝试不同的方法,有一点需要注意:subprocess就相当于fork/exec的替代方案。看起来你现在的做法并不是这样:你只是进行了fork(没有exec),并在两个进程之间交换数据——在这种情况下,使用multiprocessing
模块(在Python 2.6及以上版本中)会更合适。
使用
fcntl.fcntl(readPipe, fcntl.F_SETFL, os.O_NONBLOCK)
在调用read()之前,这样做解决了两个问题。现在,read()这个操作不再是阻塞的,而且在写入端只要调用flush(),数据就能立刻出现。
你是不是在用read()的时候没有指定大小,或者把管道当成迭代器来用(比如用for line in f
)?如果是这样,那可能就是你遇到问题的原因——read()的定义是要读取到文件的末尾才会返回,而不是只读取当前可用的数据。这就意味着它会一直等待,直到子进程调用close()。
在链接的示例代码中,这样做是可以的——父进程是以阻塞的方式运行,只是把子进程当作隔离的工具。如果你想继续进行,可以使用非阻塞的输入输出,就像你发的代码那样(但要准备好处理不完整的数据),或者分块读取(比如用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)