【问题标题】:UNIX named PIPE end of fileUNIX 命名为 PIPE 文件结尾
【发布时间】:2024-10-07 00:45:02
【问题描述】:

我正在尝试使用 unix 命名管道来输出正在运行的服务的统计信息。我打算提供一个与/proc 类似的界面,人们可以通过 catting 文件查看实时统计信息。

我在我的 python 代码中使用了与此类似的代码:

while True:
  f = open('/tmp/readstatshere', 'w')
  f.write('some interesting stats\n')
  f.close()

/tmp/readstatshere 是由mknod 创建的命名管道。

然后我用 cat 来查看统计数据:

$ cat /tmp/readstatshere
some interesting stats

大部分时间都可以正常工作。但是,如果我快速连续多次搜索条目,有时我会得到多行 some interesting stats 而不是一行。一两次,它甚至进入了一个无限循环,永远打印该行,直到我杀死它。到目前为止,我唯一的解决方法是在 f.close() 之后延迟 500 毫秒,以防止出现此问题。

我想知道为什么会发生这种情况以及是否有更好的处理方法。

提前致谢

【问题讨论】:

  • 是否有理由使用命名管道而不是常规文件?
  • @Mansour 合并完成。

标签: python linux loops pipe eof


【解决方案1】:

在这里,管道只是错误的解决方案。如果您想呈现流程内部状态的一致快照,请将其写入 临时 文件,然后将其重命名为“公共”名称。这将防止在您更新状态时其他进程读取状态可能引起的所有问题。此外,不要在繁忙的循环中执行此操作,但最好在更新之间至少休眠一秒钟的线程中执行此操作。

【讨论】:

  • 好吧,由于一些关于 linux 缓冲区缓存的死锁问题,我实际上无法做任何会导致在缓存上创建脏页的事情。这就是我选择管道的原因。
【解决方案2】:

UNIX 套接字而不是管道呢?

在这种情况下,您可以通过及时提供新数据来对每个连接做出反应。

唯一的缺点是你不能cat数据;您必须为套接字文件创建一个新的套接字句柄和connect()

MYSOCKETFILE = '/tmp/mysocket'
import socket
import os
try:
    os.unlink(MYSOCKETFILE)
except OSError: pass
s = socket.socket(socket.AF_UNIX)
s.bind(MYSOCKETFILE)
s.listen(10)
while True:
    s2, peeraddr = s.accept()
    s2.send('These are my actual data')
    s2.close()

查询此套接字的程序:

MYSOCKETFILE = '/tmp/mysocket'
import socket
import os
s = socket.socket(socket.AF_UNIX)
s.connect(MYSOCKETFILE)
while True:
    d = s.recv(100)
    if not d: break
    print d
s.close()

【讨论】:

  • 是的,我首先考虑过这一点,但我试图避免编写一个单独的程序来与套接字(在我的例子中是 TCP)通信。
【解决方案3】:

我认为你应该使用保险丝。 它有 python 绑定,见http://pypi.python.org/pypi/fuse-python/ 这使您可以编写以 posix 文件系统系统调用形式表示的问题的答案

【讨论】:

  • 这个解决方案似乎有点矫枉过正。
【解决方案4】:

不要写入实际文件。这不是/proc 所做的。 Procfs 提供了一个虚拟(非磁盘支持)文件系统,它可以按需生成您想要的信息。你可以做同样的事情,但如果它不绑定到文件系统会更容易。相反,只需在 Python 程序中运行 Web 服务,并将统计信息保存在内存中。当请求获取统计信息时,将它们形成一个漂亮的字符串并返回它们。大多数情况下,您无需浪费循环更新文件,而在下次更新之前甚至可能不会读取该文件。

【讨论】:

  • 嗯,根据我目前的理解和测试,open 上的 python 代码会阻塞,直到另一个进程打开管道进行读取 - 使用这种行为,它会 看起来像 /proc。问题是,open 有时不会阻塞,这会导致输出多个统计信息。创建一个普通的 TCP 套接字是我的第一选择,但这意味着另一个读取统计数据的小程序。我只是想让事情保持简单和最小化。
  • @Mansour 没有写入实际文件。他正在使用命名管道。 write 调用将阻塞,直到读者准备好 - 不会浪费任何周期。
【解决方案5】:

发出close 后,您需要unlink 管道。我认为这是因为存在一种竞争条件,可以在cat 完成之前再次打开管道进行读取,因此它会看到更多数据并将其读出,从而导致“一些有趣的统计数据”的倍数。

基本上你想要这样的东西:

while True:
    os.mkfifo(the_pipe)
    f = open(the_pipe, 'w')
    f.write('some interesting stats')
    f.close()
    os.unlink(the_pipe)

更新 1:致电mkfifo

更新 2:如 cmets 中所述,此代码中存在竞争条件以及多个消费者。

【讨论】:

  • 不会取消链接完全删除管道吗?谁会重新创造它?
  • 糟糕,好点子。在我的示例中,错过了对 mkfifo 的呼叫。我会更新的。
  • 好的,但是如果读者试图打开管道,os.unlinkos.mkfifo 之间会出现竞争条件。
  • 你是对的。我没有在实践中看到过那场特殊的比赛,但我只用一个消费者(cat 或其他 Python 脚本)进行了尝试。