V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
hosiet
V2EX  ›  Python

Linux 下 Python 读取命名管道的疑惑

  •  
  •   hosiet ·
    hosiet · 2015-08-04 18:33:25 +08:00 · 5836 次点击
    这是一个创建于 3402 天前的主题,其中的信息可能已经有所发展或是发生改变。

    先说需求:

    文件系统里已经有一个命名管道(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

    12 条回复    2015-08-05 09:20:38 +08:00
    yuelang85
        1
    yuelang85  
       2015-08-04 20:39:12 +08:00
    不是太理解你的问题。

    首先,堵塞了,只不过运算速度太快,所以看起来没有堵塞。

    while True这里就是不停的读取这个文件啊,并且把读取到的行打印出来,readline刚好是一行一行读下去的,你没有新的行输入,所以返回了“空”,所以就不停刷屏了。

    你判断下line是否为空,非空再打印就好了
    yuelang85
        2
    yuelang85  
       2015-08-04 20:41:24 +08:00
    另外,如果你把open放在while True外面,是看不到文件变化的
    pubby
        3
    pubby  
       2015-08-04 20:43:33 +08:00
    后面刷屏的应该是readline失败了吧,因为echo结束 myfifo写入端关闭了
    pubby
        4
    pubby  
       2015-08-04 20:44:47 +08:00
    open外面再套一层循环, readline后检测eof之类的判断
    hosiet
        5
    hosiet  
    OP
       2015-08-04 20:48:52 +08:00 via Android
    @yuelang85 实际上想实现的目的是读取到末尾管道中没有数据时,readline 函数因为没有输入又没有遇到 EOF 而阻塞,直到管道中又有数据输入,有换行符为止而使 readline 函数返回该行数据。就像是 shell 中

    cat /tmp/myfifo

    时阻塞住的情况,而不要在没数据时返回空字符串。

    open() 放在循环外面是因为打开文件只需要一次,本来就不需要多次调用 open()。
    hosiet
        6
    hosiet  
    OP
       2015-08-04 20:50:43 +08:00
    @pubby 不是很想通过轮询的方式判断有没有数据,占用 CPU 时间有点多
    akira
        7
    akira  
       2015-08-04 20:53:31 +08:00
    @hosiet 所以你需要的是非阻塞的模式
    pubby
        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结束了。
    hosiet
        9
    hosiet  
    OP
       2015-08-05 03:14:43 +08:00 via Android
    @pubby 感谢,这个解释可能说明了一些问题。

    按我理解,现在这种应用应当存在两种情况:

    第一种:Python 程序打开 FIFO 读,有另外的程序打开 FIFO 写,并且没有写完,此时读取文件在默认(阻塞)条件下会阻塞。但是如果以非阻塞模式打开文件读会怎么样呢?(待实验,是否出现 EAGAIN/EWOULDBLOCK?)

    第二种:Python 程序打开 FIFO 读,已经有程序打开过 FIFO 写并关闭了文件,此时一直读取文件一定会碰到 EOF,之后再以任何方式读取 FIFO 就像是读取普通文件遇到文件末尾后继续读返回空字符串那样返回了空字符串而非阻塞。是这样吗?如果以非阻塞方式打开文件读又会如何?(待实验)

    @akira 使用非阻塞方式,是在 Python 程序中以非阻塞方式打开文件读吗?我觉得并不符合情景要求,因为任何情况下 read 都会立刻返回。
    pubby
        10
    pubby  
       2015-08-05 03:41:10 +08:00 via Android
    如果没有读,你根本没机会去写入的
    hosiet
        11
    hosiet  
    OP
       2015-08-05 08:26:13 +08:00
    @pubby 的确是这样的,如果没有一个以读模式打开FIFO文件的进程,那么其它进程以写模式打开文件会卡在 open() 中。但是在现在的情况下始终有一个以读模式打开文件的进程,所以并不存在问题。

    我只是想要一个符合情景的解决方案 =_=
    hosiet
        12
    hosiet  
    OP
       2015-08-05 09:20:38 +08:00
    好了,找到了一个 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 反应。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2905 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 35ms · UTC 00:30 · PVG 08:30 · LAX 16:30 · JFK 19:30
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.