问题描述
我正在linux上运行python3.8。
在我的脚本中,创建一个命名管道,并按如下所示打开它:
import os
import posix
import time
file_name = 'fifo.txt'
os.mkfifo(file_name)
f = posix.open(file_name,os.O_RDWR | os.O_NONBLOCK)
os.set_blocking(f,False)
在尚未打开文件以供其他地方读取的情况下(例如,使用cat
),我开始循环写入文件。
base_line = 'abcdefghijklmnopqrstuvwxyz'
s = base_line * 10000 + '\n'
while True:
try:
posix.write(f,s.encode())
except BlockingIOError as e:
print("Exception occurred: {}".format(e))
time.sleep(.5)
然后我使用cat
从命名管道中读取时,发现发生了部分写入。
我很困惑如何知道在此实例中写入了多少字节。由于引发了异常,因此我无权访问返回值(写入的字节数)。该文档建议BlockingIOError
具有一个名为characters_written
的属性,但是当我尝试访问该字段时,会引发AttributeError
。
总而言之:如何首先避免这种部分写入,或者至少知道在这种情况下部分写入了多少?
解决方法
os.write
执行无缓冲写入。文档指出,BlockingIOError
仅在缓冲的写操作将阻塞时才具有characters_written
属性。
如果在管道变满之前成功写入了任何字节,那么将从os.write
返回该字节数。否则,您将获得一个例外。当然,即使写入了一些字节,诸如驱动器故障之类的事情也会导致异常。这与POSIX write
的工作方式没有什么不同,除了引发错误以外,不是返回-1来返回错误。
如果您不喜欢处理异常,则可以在文件描述符周围使用包装器,例如io.FileIO
对象。我已经修改了您的代码,因为它会在您每次循环回到os.write
调用时尝试写入整个缓冲区(如果一次失败,则每次都会失败):
import io
import os
import time
base_line = 'abcdefghijklmnopqrstuvwxyz'
data = (base_line * 10000 + '\n').encode()
file_name = 'fifo.txt'
os.mkfifo(file_name)
fd = os.open(file_name,os.O_RDWR | os.O_NONBLOCK)
# os.O_NONBLOCK makes os.set_blocking(fd,False) unnecessary.
with io.FileIO(fd,'wb') as f:
written = 0
while written < len(data):
n = f.write(data[written:])
if n is None:
time.sleep(.5)
else:
written += n
顺便说一句,您可以使用selectors
模块而不是time.sleep
;我注意到尝试从管道读取数据时会出现一些延迟,因为存在睡眠延迟,如果您使用selectors
模块,则不会发生这种延迟:
with io.FileIO(fd,'wb') as f:
written = 0
sel = selectors.DefaultSelector()
sel.register(f,selectors.EVENT_WRITE)
while written < len(data):
n = f.write(data[written:])
if n is None:
# Wait here until we can start writing again.
sel.select()
else:
written += n
sel.unregister(f)
在POSIX named pipe (fifo) drops record in nonblocking mode的答案中也可以找到一些有用的信息。