UNIX 命名管道文件结束符

3

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

在我的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

大部分时间运行良好。然而,如果我快速连续地cat该条目几次,有时会得到多行“一些有趣的统计数据”,而不是一行。偶尔会陷入无限循环,一直打印该行,直到我杀掉它。目前我唯一的解决方法是在f.close()之后延迟500毫秒,以防止出现这个问题。
我想知道为什么会出现这种情况,是否有更好的处理方法。
提前感谢。

使用命名管道与普通文件相比,有什么理由吗? - Vaughn Cato
@Mansour 合并完成。 - Tim Post
5个回答

1

使用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)通信的单独程序。 - Mansour

1
一个管道在这里显然不是正确的解决方案。如果你想要呈现一个进程内部状态的一致快照,那么请将其写入一个临时文件,然后将其重命名为“公共”名称。这将防止其他进程在你更新状态时读取状态可能引起的所有问题。另外,请不要在繁忙的循环中实现此操作,而是最好使用睡眠至少一秒钟的线程进行更新。

由于涉及到Linux缓存的死锁问题,我无法执行任何会在缓存中创建脏页的操作。这就是为什么我选择了一个管道。 - Mansour

1

这个解决方案似乎有些过度设计了。 - Mansour

0

在执行 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:如评论中所述,此代码存在多个消费者的竞争条件。


unlink会不会彻底删除管道?谁会重新创建它呢? - Mansour
哎呀,说得对。在我的例子中漏掉了调用 mkfifo。我会更新它。 - dowski
如果一个读取器尝试打开管道,则os.unlinkos.mkfifo之间会存在竞争条件。 - Mansour
你说得对。我没有在实践中看到过那个特定的竞争条件,但我只是尝试了一个单一的消费者(无论是cat还是另一个Python脚本)。 - dowski

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

从我的理解和目前的测试来看,Python代码块在open上阻塞,直到另一个进程打开管道进行读取 - 这种行为看起来像/proc。问题是,open有时不会阻塞,这会导致输出多个stats。创建一个普通的TCP套接字是我的首选,但这意味着需要另一个小程序来读取stats。我只想保持简单和最小化。 - Mansour
3
@Mansour没有向实际文件写入内容,而是使用了一种命名管道。当读取器已准备好时,write调用将被阻塞 - 不会浪费任何周期。 - dowski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接