我正在尝试编写一个“更干净”的程序来释放一个在命名管道中被阻塞的潜在编写器(因为没有读取器从管道中读取)。但是,当没有编写器被阻止写入管道时,清理器本身不应阻止。换句话说,“清洁工”必须立即返回/终止,无论是否有被封锁的作家。
因此我搜索了“从命名管道读取Python非阻塞”,并得到了这些:
似乎他们建议只使用os.open(file_name, os.O_RDONLY | os.O_NONBLOCK)
应该没问题,这在我的机器上并不真正起作用。我想我可能搞砸了某个地方或误解了他们的一些建议/情况。但是,我真的无法弄清楚自己出了什么问题。
我找到了Linux手册页(http://man7.org/linux/man-pages/man2/open.2.html),O_NONBLOCK的解释似乎与他们的建议一致,但与我在机器上的观察不一致...
万一它是相关的,我的操作系统是 Ubuntu 14.04 LTS 64位。
这是我的代码:
import os
import errno
BUFFER_SIZE = 65536
ph = None
try:
ph = os.open("pipe.fifo", os.O_RDONLY | os.O_NONBLOCK)
os.read(ph, BUFFER_SIZE)
except OSError as err:
if err.errno == errno.EAGAIN or err.errno == errno.EWOULDBLOCK:
raise err
else:
raise err
finally:
if ph:
os.close(ph)
(不知道如何进行Python语法高亮...)
最初只有第二个raise
,但我发现os.open
和os.read
虽然没有阻止,但也没有引发任何异常......我真的不是知道作者将写入缓冲区的程度!如果非阻塞read
没有引发异常,我该如何知道何时停止阅读?
2016年8月8日更新:
这似乎是满足我需求的解决方法/解决方案:
import os
import errno
BUFFER_SIZE = 65536
ph = None
try:
ph = os.open("pipe.fifo", os.O_RDONLY | os.O_NONBLOCK)
while True:
buffer = os.read(ph, BUFFER_SIZE)
if len(buffer) < BUFFER_SIZE:
break
except OSError as err:
if err.errno == errno.EAGAIN or err.errno == errno.EWOULDBLOCK:
pass # It is supposed to raise one of these exceptions
else:
raise err
finally:
if ph:
os.close(ph)
它会在read
上循环播放。每次读取内容时,它会将读取的内容大小与指定的BUFFER_SIZE
进行比较,直到达到EOF(然后编写器将解锁并继续/退出)。
我仍然想知道为什么read
没有引发异常。
2016年10月8日更新:
为了说清楚,我的总体目标是这样的。
我的主程序(Python)有一个线程作为读者。它通常在命名管道上阻塞,等待“命令”。有一个编写器程序(Shell脚本),它将在每次运行中向同一个管道写一个单行“命令”。
在某些情况下,作者在我的主程序启动之前或主程序终止之后启动。在这种情况下,编写器将阻塞等待读者的管道。这样,如果稍后我的主程序启动,它将立即从管道中读取以从被阻止的编写器获得“命令” - 这不是我想要的。我希望我的程序忽略在它之前开始的作家。
因此,我的解决方案是,在我的读者线程初始化期间,我执行非阻塞读取以释放编写器,而不是真正执行他们试图写入管道的“命令”。
答案 0 :(得分:0)
此解决方案不正确。
while True:
buffer = os.read(ph, BUFFER_SIZE)
if len(buffer) < BUFFER_SIZE:
break
这实际上并不会读取所有内容,只有在读取部分内容后才能读取。请记住:您只能保证使用常规文件填充缓冲区,在所有其他情况下,可以在EOF之前获取部分缓冲区。执行此操作的正确方法是循环,直到达到文件的实际结尾,这将给出长度为0的读取。文件结尾表示没有编写器(它们都已退出或关闭了fifo)。
while True:
buffer = os.read(ph, BUFFER_SIZE)
if not buffer:
break
但是,面对非阻塞IO,这将无法正常工作。事实证明,这里完全不需要非阻塞IO。
import os
import fcntl
h = os.open("pipe.fifo", os.O_RDONLY | os.O_NONBLOCK)
# Now that we have successfully opened it without blocking,
# we no longer want the handle to be non-blocking
flags = fcntl.fcntl(h, fcntl.F_GETFL)
flags &= ~os.O_NONBLOCK
fcntl.fcntl(h, fcntl.F_SETFL, flags)
try:
while True:
# Only blocks if there is a writer
buf = os.read(h, 65536)
if not buf:
# This happens when there are no writers
break
finally:
os.close(h)
导致此代码阻止的唯一方案是,是否有一个活动的编写器已打开fifo但未写入它。从你所描述的内容来看,听起来并非如此。
您的计划希望根据具体情况做两件事:
如果没有作家,请立即返回。
如果有写入器,则从FIFO中读取数据,直到写入器完成。
非阻止read()
对任务#1的无效。无论您是否使用O_NONBLOCK
,read()
都会在情况#1中立即返回 。所以唯一的区别在于情况#2。
在情况#2中,你的程序的目标是从作者那里读取整个数据块。这正是阻止IO工作的方式:它等待编写器完成,然后read()
返回。如果操作无法立即完成,那么非阻塞IO的重点就是提前返回,这与您的程序的目标相反 - 即等到操作完成。
如果您使用非阻塞read()
,在情况#2中,您的程序有时会在作者完成作业之前提前返回。或者也许你的程序会在从FIFO读取一半命令后返回,而另一半(现已损坏)则在那里。您的问题表达了这种担忧:
如果非阻塞读取没有引发异常,我该如何知道何时停止读取?
您知道何时停止读取,因为read()
在所有编写器关闭管道时返回零字节。 (方便的是,如果首先没有编写者,也会发生这种情况。)遗憾的是,如果编写者在完成时没有关闭管道的末尾会发生这种情况。如果编写器在完成时关闭管道,则更简单,更直接,因此这是推荐的解决方案,即使您需要稍微修改编写器。如果作者无论出于何种原因都无法关闭管道,那么解决方案就更复杂了。
非阻塞read()
的主要用例是,当IO在后台运行时,您的程序还有其他任务需要完成。
答案 1 :(得分:0)
在POSIX C程序中,如果read()尝试从空管道或FIFO特殊文件中读取,则它将具有以下结果之一:
因此,首先检查是否还有写程序仍在打开fifo进行写操作。如果没有,则读取将得到一个空字符串,并且没有异常。否则,将引发异常