我试图编写一个“cleaner”程序来释放一个潜在的writer,这个writer在命名管道处被阻塞(因为没有读卡器正在读取管道)。但是,当没有写入程序被阻止向管道写入数据时,清理程序本身不应被阻止。换句话说,“cleaner”必须立即返回/终止,无论是否有被阻止的writer。
因此,我搜索了“Python non-blocking read from named pipe”,得到了以下结果:
How to read named FIFO non-blockingly?
fifo - reading in a loop
What conditions result in an opened, nonblocking named pipe (fifo) being "unavailable" for reads?
Why does a read-only open of a named pipe block?
似乎他们建议简单地使用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.04lts 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.openos.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)有一个线程作为阅读器。它通常阻塞命名管道,等待“命令”。有一个writer程序(Shell脚本)将在每次运行时向同一管道写入一行“命令”。
在某些情况下,编写器在主程序启动之前或主程序终止之后启动。在这种情况下,写入程序将阻塞管道以等待读取器。这样,如果稍后我的主程序启动,它将立即从管道中读取以从被阻止的写入程序获取“命令”-这不是我想要的。我希望我的程序忽略之前启动的编剧。
因此,我的解决方案是,在reader线程初始化期间,我执行非阻塞读取以释放writer,而不真正执行它们试图写入管道的“命令”。

最佳答案

此解决方案不正确。

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。从你所描述的情况来看,情况并非如此。
非阻塞IO不会这样做
你的程序需要做两件事,具体取决于环境:
如果没有作者,请立即返回。
如果有写入程序,请从FIFO读取数据,直到写入程序完成。
非阻塞性read()对任务1没有任何影响。无论您是否使用O_NONBLOCK,在情况1下,read()将立即返回。因此,唯一的区别在于情境2。
在情况2中,程序的目标是从编写器读取整个数据块。这正是阻塞IO的工作原理:它等待写入程序完成,然后read()返回。如果操作不能立即完成,非阻塞IO的整个要点是尽早返回,这与程序的目标相反,即等待操作完成。
如果在情况2中使用非阻塞read(),则程序有时会在编写者完成工作之前提前返回。或者你的程序会在从FIFO读取一半命令后返回,剩下一半(现在已经损坏)在那里。你的问题表达了这一关切:
如果非阻塞读取没有引发异常,我应该如何知道何时停止读取?
您知道何时停止读取,因为当所有写入程序都关闭管道时,read()返回零字节。(方便的是,如果一开始没有写入程序,也会发生这种情况。)不幸的是,如果写入程序在完成时没有关闭其末端,就不会发生这种情况。如果编写器在完成时关闭管道,则会简单得多,也更简单,因此这是建议的解决方案,即使您需要稍微修改编写器。如果编剧们因为任何原因不能关闭管道,解决方案就更复杂了。
非阻塞read()的主要用例是,当IO在后台运行时,程序是否还有其他任务要完成。

关于python - O_NONBLOCK不会在Python中引发异常,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/38843278/

10-15 02:10