我在FIFO上有一个读写器,读者不能无限期地阻塞。为此,我使用O_NONBLOCK
打开阅读结束。
写端可以阻塞,因此我将其作为常规文件打开。大写无法接受非常 - 读取/写入4MB块需要几分钟而不是预期的一秒钟(预期,因为在linux中相同的代码只需要几分之一秒)。
Python中复制问题的示例代码。首先,使用mkfifo
创建一个fifo,例如mkfifo some_fifo
,然后运行阅读结束,然后是写作结束。
阅读结束:
import os, time
# mkfifo some_fifo before starting python
fd = os.open('some_fifo',os.O_RDONLY | os.O_NONBLOCK)
while True:
try:
read = len(os.read(fd, 8192)) # read up to 8kb (FIFO buffer size in mac os)
print(read)
should_block = read < 8192 # linux
except BlockingIOError:
should_block = True # mac os
if should_block:
print('blocking')
time.sleep(0.5)
写完:
import os
fd = os.open('some_fifo',os.O_WRONLY)
os.write(fd, b'aaaa'*1024*1024) # 4MB write
注意:我遇到此问题的原始代码是也在linux上运行的跨平台Java代码。不幸的是,这意味着我不能将kqueue与kevent的data
字段一起使用来计算出我可以在不阻塞的情况下读取多少内容 - 这些数据在我使用的epoll / kqueue的抽象中丢失了。这意味着使用阻塞fd à la this answer的解决方案是不可接受的。
编辑:原始代码使用kqueue来阻止读取端的文件描述符,执行更糟
编辑2 :Linux os.read()
在连接管道的另一端之前没有抛出BlockingIOError,尽管文档声明它应该(调用成功(返回0)但是将errno设置为EAGAIN)。更新了代码以便对linux行为友好。
编辑3 :macOS的代码最初是:
import select, os
# mkfifo some_fifo before starting python
fd = os.open('some_fifo',os.O_RDONLY | os.O_NONBLOCK)
kq = select.kqueue()
ke = select.kevent(fd)
while True:
try:
read = len(os.read(fd, 8192)) # read up to 8kb (FIFO buffer size in mac os)
except BlockingIOError:
evts = kq.control([ke], 1, 10) # 10-second timeout, wait for 1 event
print(evts)
这与具有睡眠的版本一样糟糕,但是睡眠确保问题不在于阻塞机制,而且是跨平台的。