Python 管道 - 增量读取输出时会发生啥

Posted

技术标签:

【中文标题】Python 管道 - 增量读取输出时会发生啥【英文标题】:Python Pipes - What Happens When Reading Output IncrementallyPython 管道 - 增量读取输出时会发生什么 【发布时间】:2009-10-23 09:33:58 【问题描述】:

根据a section中的this presumably accurate book,

管道的一个常见用途是读取 增量压缩文件;那 是,没有解压缩整个 一下子。以下函数 将压缩文件的名称作为 一个参数并返回一个管道 使用 gunzip 解压 内容:

 def open_gunzip(filename):
     cmd = 'gunzip -c ' + filename
     fp = os.popen(cmd)
     return fp

如果你从 fp 中读取一行 时间,您永远不必存储 内存中或上的未压缩文件 磁盘。

也许我只是解释错了,但我不明白这怎么可能。 Python 无法在吐出结果的过程中暂停 gunzip,对吧?我假设 gunzip 在继续输出更多行之前读取一行输出之前不会阻塞,因此必须有一些缓冲区捕获所有这些(无论是在 Python 解释器中还是在操作系统中,无论是在内存中还是在磁盘上),这意味着未压缩的文件被完整地存储在某处...对吗?

【问题讨论】:

永远不要使用os.popen。它已被弃用。请改用subprocess 模块。 【参考方案1】:

你的假设是错误的。 gunzip 不必查看整个文件即可解压缩它。阅读解压缩文件格式。有一个目录,其中包含各个组件的偏移量。

可以分段解压文件。

“未压缩的文件正在完整存储在某处......对吗?”

不一定。不知道你为什么假设它或你在哪里读到它。

所有低级 I/O 调用都可以阻塞。 gunzip 中的写入(写入管道时)可能会在管道缓冲区已满时阻塞。这就是定义管道 I/O 的方式。管道 I/O 块。

查看管道的手册页以获取详细信息。

如果一个进程试图从一个 空管道,然后 read(2) 将 阻塞直到数据可用。如果 一个进程尝试写入一个 满管(见下文),然后写(2) 阻塞直到有足够的数据 从管道中读取以允许 写完成。非阻塞 使用 fcntl(2) 可以实现 I/O F_SETFL 操作启用 O_NONBLOCK 打开文件状态标志。

【讨论】:

【参考方案2】:

这真的来自gunzip 实现,而不是来自python。 它是用 C 编写的。它可能使用 C 的 stdio.h 中的 fwrite() 来编写它的输出。

libc6 实现我使用自动创建一个输出缓冲区,当它被填满时,阻塞fwrite(),直到它可以写更多。

【讨论】:

关键是当gunzip调用write()时,阻塞发生在内核中。无论您的程序是用 C 还是 Python 编写的,甚至根本使用 libc,都会发生这种情况。【参考方案3】:

暂停gunzip 的不是Python,而是内核在尝试写入(使用write() 系统调用)到完整缓冲区时将停止执行gunzip。这称为阻塞 IO。内核维护一个连接管道两端的内部缓冲区,独立于任何正在写入或读取管道的进程中发生的任何缓冲。

当从具有空缓冲区(即当前没有来自gunzip 的任何数据写入其中)的管道读取数据时,Python 也会同样阻塞。

管道可以看作是Producer-consumer problem 的解决方案。

【讨论】:

以上是关于Python 管道 - 增量读取输出时会发生啥的主要内容,如果未能解决你的问题,请参考以下文章

增量式pid和位置式pid相比各有啥优缺点

我们如何修复 ADLS Gen 2 中的增量位置文件

Databricks - 从增量表写入流到 orc 文件的读取流仅具有更改

Azure Synapse 管道:如何将增量更新从 SQL Server 移动到 Synapse 以处理数字

“增量负载”是啥意思?

当我将任何命令通过管道传输到 telnet 会话时会发生啥,为啥会话会关闭