如何(我可以)问一个PIPE可以读取多少字节?

时间:2013-11-19 17:28:35

标签: python subprocess popen nonblocking

我在Python中实现了一个非阻塞读取器,我需要提高它的效率。

背景:我需要从一个子进程读取大量输出(以Popen()开头)并传递给另一个线程。读取该子进程的输出不得阻塞超过几毫秒(最好是读取可用字节所需的时间很短)。

目前,我有一个实用程序类,它接受文件描述符(stdout)和超时。我select()readline(1)直到发生以下三种情况之一:

  1. 我读过换行符
  2. 我的超时(几毫秒)到期
  3. select告诉我没有什么可读的文件描述符。
  4. 然后我将缓冲的文本返回给调用方法,调用方法用它来完成。

    现在,对于真正的问题:因为我正在阅读如此多的输出,我需要提高效率。我想通过询问文件描述符有多少字节待处理然后readline([that many bytes])来做到这一点。它应该只是传递东西,所以我实际上并不关心换行的位置,或者即使有任何问题。 我可以问一下文件描述符有多少字节可供阅读,如果是,怎么做?

    我已经做了一些搜索,但是我很难找到要搜索的内容,更不用说是否可能了。

    即使是正确方向的一点也会有所帮助。

    注意:我正在开发Linux,但这对于“Pythonic”解决方案来说无关紧要。

4 个答案:

答案 0 :(得分:5)

在Linux上,os.pipe()只是管道(2)的包装器。两者都返回一对文件描述符。通常会使用lseek(2)(Python中的os.lseek())来重新定位文件decsriptor的偏移量,以获取可用数据量。但是,并非所有文件描述符都能够搜索。

在Linux上尝试管道上的lseek(2)将返回错误,请参阅manual page。这是因为管道或多或少是生产者和数据消费者之间的缓冲区。该缓冲区的大小取决于系统。

在Linux上,a pipe has a 64 kB buffer,这是您可以获得的最多数据。

编辑:如果您可以更改子进程的工作方式,您可以考虑使用内存映射文件或一块很好的大块共享内存。

Edit2 :使用polling objects可能比选择更快。

答案 1 :(得分:0)

这个问题似乎提供了一个可能的解决方案,但它可能需要重组。

Non-blocking read on a subprocess.PIPE in python

否则,我假设您知道一次读取N个字节的数据:

all_data = ''
while True:
    data = pipe.read(1024)   # Reads 1024 bytes or to end of pipe
    if not data:
        break
    all_data += data
    # Add your timeout break here

答案 2 :(得分:0)

您可以通过调用os.fstat(file_descriptor)并检查st_size属性(即写入的字节数)来找到答案。

import os
reader_file_descriptor, writer_file_descriptor = os.pipe()
os.write(writer_file_descriptor, b'I am some data')
readable_bytes = os.fstat(writer_file_descriptor).st_size

答案 3 :(得分:0)

我已经根据 spacether 的回答中的想法实现了这一点

ReactNative.NativeModules.LottieAnimationView.resume did not have a corresponding prop defined
in the mock provided to SafeModule.

ReactNative.NativeModules.LottieAnimationView.pause did not have a corresponding prop defined
in the mock provided to SafeModule.

....

import select
import os

def readLen(p):
    # works on mac, might work on Linux, probably doesn't on windows (maybe return 1 in that case)
    size = os.fstat(p.fileno()).st_size
    return size

def readIfAny(p, timeout=1, default=None):
    if select.select([p], [], [], timeout)[0]:
        size = readLen(p)
        if size:
            return p.read(size)
    return default

请注意,我在某些地方读到过,您应该尽量避免像这样直接读取和写入子进程管道,以避免死锁。但这是我目前找到的最安全的方法。

注意 2:我认为 sys.stdin.read 将在 eof 上返回 b'' 或 ''。这似乎没有引发任何异常,我仍然不知道如何判断它何时完成。

注意 3:根据它们打开的模式,您会得到字节或字符串。它也适用于标准输入、标准输出和标准错误。