先说需求:
文件系统里已经有一个命名管道(FIFO),偶尔有其它程序对其进行写入,需要将这些写入由 Python 程序进行读取,一次读一行内容。
我的最初想法是程序不断地读取FIFO(f.readline()),由于命名管道是阻塞的,读完之后再继续读取应该会阻塞,这样其它程序继续写入一行的时候 Python 程序会立刻读到数据。
所以写了一个小脚本做实验:
FILEPATH = '/tmp/myfifo'
f = open(FILEPATH, 'r')
while True:
line = f.readline()
print(line)
但是效果是程序首先在 open() 处阻塞(这是 fifo 的特性,可以理解)。之后如果使用 echo 向 fifo 写入数据:
echo "abc" > /tmp/myfifo
那么程序会立刻打印出 'abc',然后被空字符串刷屏。如何解决 f.readline() 没有阻塞反而返回了一个空字符串的问题呢?
PS:Google 上搜索到的全都是使用非阻塞的方法,看了一圈也没有找到解决方法。使用 os.read() 或者 os.open() 试了试貌似也没有效果。
平台是 Linux 3.19 / Python 3.4.3
1
yuelang85 2015-08-04 20:39:12 +08:00
不是太理解你的问题。
首先,堵塞了,只不过运算速度太快,所以看起来没有堵塞。 while True这里就是不停的读取这个文件啊,并且把读取到的行打印出来,readline刚好是一行一行读下去的,你没有新的行输入,所以返回了“空”,所以就不停刷屏了。 你判断下line是否为空,非空再打印就好了 |
2
yuelang85 2015-08-04 20:41:24 +08:00
另外,如果你把open放在while True外面,是看不到文件变化的
|
3
pubby 2015-08-04 20:43:33 +08:00
后面刷屏的应该是readline失败了吧,因为echo结束 myfifo写入端关闭了
|
4
pubby 2015-08-04 20:44:47 +08:00
open外面再套一层循环, readline后检测eof之类的判断
|
5
hosiet OP @yuelang85 实际上想实现的目的是读取到末尾管道中没有数据时,readline 函数因为没有输入又没有遇到 EOF 而阻塞,直到管道中又有数据输入,有换行符为止而使 readline 函数返回该行数据。就像是 shell 中
cat /tmp/myfifo 时阻塞住的情况,而不要在没数据时返回空字符串。 open() 放在循环外面是因为打开文件只需要一次,本来就不需要多次调用 open()。 |
8
pubby 2015-08-04 20:55:48 +08:00
@hosiet 我的意思是 echo "abc" >> /tmp/myfifo 干了3件事情
1. w方式打开myfifo 2. 写入abc\n 3. close myfifo 因此 f.readline()后肯定遇到eof了 你可以试验一下的 $ mkfifo myfifo $ cat myfifo 另外开个shell $ echo "abc" >> myfifo 可以看到第一个shell中 cat结束了。 |
9
hosiet OP @pubby 感谢,这个解释可能说明了一些问题。
按我理解,现在这种应用应当存在两种情况: 第一种:Python 程序打开 FIFO 读,有另外的程序打开 FIFO 写,并且没有写完,此时读取文件在默认(阻塞)条件下会阻塞。但是如果以非阻塞模式打开文件读会怎么样呢?(待实验,是否出现 EAGAIN/EWOULDBLOCK?) 第二种:Python 程序打开 FIFO 读,已经有程序打开过 FIFO 写并关闭了文件,此时一直读取文件一定会碰到 EOF,之后再以任何方式读取 FIFO 就像是读取普通文件遇到文件末尾后继续读返回空字符串那样返回了空字符串而非阻塞。是这样吗?如果以非阻塞方式打开文件读又会如何?(待实验) @akira 使用非阻塞方式,是在 Python 程序中以非阻塞方式打开文件读吗?我觉得并不符合情景要求,因为任何情况下 read 都会立刻返回。 |
10
pubby 2015-08-05 03:41:10 +08:00 via Android
如果没有读,你根本没机会去写入的
|
11
hosiet OP @pubby 的确是这样的,如果没有一个以读模式打开FIFO文件的进程,那么其它进程以写模式打开文件会卡在 open() 中。但是在现在的情况下始终有一个以读模式打开文件的进程,所以并不存在问题。
我只是想要一个符合情景的解决方案 =_= |
12
hosiet OP 好了,找到了一个 dirty hack:
import os, time import multiprocessing FM_FILEPATH = '/tmp/myfifo' def mf_keeper(filepath=FM_FILEPATH): """ Open it. Don't close it. """ f2 = open(filepath, 'w') while True: time.sleep(60) pass if __name__ == "__main__": p = multiprocessing.Process(target=mf_keeper) p.start() # never join f = open(FM_FILEPATH, 'r') while True: line = f.readline() print("I have \"{}\"".format(line)) 似乎 f.readline() 读取时候只会对第一个打开FIFO的进程所写入的 EOF 反应。 |