了解 Python 中的命名管道 (FIFO)

Posted

技术标签:

【中文标题】了解 Python 中的命名管道 (FIFO)【英文标题】:Understanding named Pipes (FIFO) in Python 【发布时间】:2015-12-02 15:08:28 【问题描述】:

我在 Unix 环境中运行 Python 2.7(在 Ubuntu 和 OSX 上测试)

我有以下程序:


使用 os.open():

[脚本 1]

import os

pipe_1_name = "pipe_1"
pipe_2_name = "pipe_2"
pipe_3_name = "pipe_3"


def set_connection():
    pipe_names = [pipe_1_name, pipe_2_name, pipe_3_name]
    for pipe_name in pipe_names:
        if os.path.exists(pipe_name):
            os.remove(pipe_name)
            os.mkfifo(pipe_name)
        else:
            os.mkfifo(pipe_name)

    pipe_1 = os.open(pipe_1_name, os.O_WRONLY)
    os.write(pipe_1, "server_message_0\n")

    pipe_2 = open(pipe_2_name, 'r')
    received = pipe_2.readline()[:-1]
    print "[0] Now processing if received is correct: " + received

    pipe_3 = open(pipe_3_name, 'r')
    received = pipe_3.readline()[:-1]
    print "[1] Now processing if received is correct: " + received

    print "Connection established."
    return pipe_1,pipe_2,pipe_3


def main():
    pipe_1, pipe_2, pipe_3 = set_connection()

    print str(pipe_1)
    print str(pipe_2)
    print str(pipe_3)

if __name__ == "__main__":
    main()

[脚本 2]

import os

pipe_1_name = "pipe_1"
pipe_2_name = "pipe_2"
pipe_3_name = "pipe_3"


def get_connection():
    pipe_names = [pipe_1_name, pipe_2_name, pipe_3_name]
    for pipe_name in pipe_names:
        if not os.path.exists(pipe_name):
            raise Exception("Pipe "+pipe_name+" does not exist!")

    pipe_1 = open(pipe_1_name, 'r')
    received = pipe_1.readline()[:-1]
    print "[0] Now processing if received is correct: " + received

    pipe_2 = os.open(pipe_2_name, os.O_WRONLY)
    os.write(pipe_2, "client_message_0\n")

    pipe_3 = os.open(pipe_3_name, os.O_WRONLY)
    os.write(pipe_3, "client_message_1\n")

    print "Connection established."
    return pipe_1,pipe_2,pipe_3


def main():
    pipe_1, pipe_2, pipe_3 = get_connection()

    print str(pipe_1)
    print str(pipe_2)
    print str(pipe_3)

if __name__ == "__main__":
    main()

逻辑很简单:

[管道 1] 1. 脚本 1 打开脚本 2 的写入管道。 2. 脚本 2 从管道中读取。 [管道 2] 3. 脚本 2 打开到脚本 1 的写入管道。 4. 脚本 1 从管道中读取。 [管道 3] 5. 脚本 2 打开脚本 1 的写入管道。 6. 脚本 1 从管道中读取。

完全按预期工作。


这就是问题所在。我不想使用 os.open()。我想接收一个文件对象并用它来与管道接口。显然,这不是不可能的,因为我可以从带有文件对象的管道中读取。但是,以下脚本不起作用。


没有 os.open()

[脚本 1]

import os

pipe_1_name = "pipe_1"
pipe_2_name = "pipe_2"
pipe_3_name = "pipe_3"


def set_connection():
    pipe_names = [pipe_1_name, pipe_2_name, pipe_3_name]
    for pipe_name in pipe_names:
        if os.path.exists(pipe_name):
            os.remove(pipe_name)
            os.mkfifo(pipe_name)
        else:
            os.mkfifo(pipe_name)

    pipe_1 = open(pipe_1_name, 'w')
    pipe_1.write("server_message_0\n")

    pipe_2 = open(pipe_2_name, 'r')
    received = pipe_2.readline()[:-1]
    print "[0] Now processing if received is correct: " + received

    pipe_3 = open(pipe_3_name, 'r')
    received = pipe_3.readline()[:-1]
    print "[1] Now processing if received is correct: " + received

    print "Connection established."
    return pipe_1,pipe_2,pipe_3


def main():
    pipe_1, pipe_2, pipe_3 = set_connection()

    print str(pipe_1)
    print str(pipe_2)
    print str(pipe_3)

if __name__ == "__main__":
    main()

[脚本 2]

import os

pipe_1_name = "pipe_1"
pipe_2_name = "pipe_2"
pipe_3_name = "pipe_3"


def get_connection():
    pipe_names = [pipe_1_name, pipe_2_name, pipe_3_name]
    for pipe_name in pipe_names:
        if not os.path.exists(pipe_name):
            raise Exception("Pipe "+pipe_name+" does not exist!")

    pipe_1 = open(pipe_1_name, 'r')
    received = pipe_1.readline()[:-1]
    print "[0] Now processing if received is correct: " + received

    pipe_2 = open(pipe_2_name, 'w')
    pipe_2.write("client_message_0\n")

    pipe_3 = open(pipe_3_name, 'w')
    pipe_3.write("client_message_1\n")

    print "Connection established."
    return pipe_1,pipe_2,pipe_3


def main():
    pipe_1, pipe_2, pipe_3 = get_connection()

    print str(pipe_1)
    print str(pipe_2)
    print str(pipe_3)

if __name__ == "__main__":
    main()

它们看起来一样,不是吗?唯一的区别是我如何打开fifo。而不是os.open(pipe_name,os.O_WRONLY),我使用pipe = open(pipe_name, 'w')


在不使用 os.open() 的第二组脚本中发生了什么,脚本 1 在 pipe_2 = open(pipe_2_name, 'r') 处阻塞,而脚本 2 在 pipe_2 = open(pipe_2_name, 'w') 处阻塞。

为什么会这样?

对不起,文字墙。我真的很困惑这个问题。

【问题讨论】:

【参考方案1】:

在第二组脚本中发生了什么,那些不使用的脚本 os.open(),脚本 1 在 pipe_2 = open(pipe_2_name, 'r') 处阻塞,而脚本 2 在 pipe_2 = open(pipe_2_name, 'w') 处阻塞。

不,在received = pipe_1.readline()[:-1] 处编写 2 个块的脚本。

为什么会这样?

这是因为脚本 1 的 open(pipe_1_name, 'w') 导致写入的消息以固定大小的块(通常为 4096 或 8192 字节)进行缓冲,因此 pipe_1.write("server_message_0\n") 还没有向管道写入任何内容,而只是写入缓冲区,并且脚本 2 没有任何内容可供阅读。请参阅open() 和How often does python flush to a file?

要解决这个问题,由于您的消息是完整的行,因此使用 行缓冲 就足够了,例如。 g.

    pipe_1 = open(pipe_1_name, 'w', 1)

(以及其他写入管道)。

【讨论】:

以上是关于了解 Python 中的命名管道 (FIFO)的主要内容,如果未能解决你的问题,请参考以下文章

Python 从命名管道/FIFO 中读取 JSON

多处理是不是支持命名管道(FIFO)?

linux 进程间通信 命名管道FIFO的原理与使用

命名管道FIFO

进程间通信--FIFO(命名管道)

Linux系统编程——进程间通信:命名管道(FIFO)