Python管道 - 逐步读取输出时发生了什么
根据这本书中的一部分内容,
管道的一个常见用法是逐步读取压缩文件;也就是说,不需要一次性解压整个文件。下面这个函数接受一个压缩文件的名字作为参数,并返回一个管道,这个管道使用gunzip来解压内容:
def open_gunzip(filename): cmd = 'gunzip -c ' + filename fp = os.popen(cmd) return fp
如果你一次从fp读取一行,你就不需要在内存或磁盘上存储解压后的整个文件。
也许我理解错了,但我看不出这是怎么可能的。Python应该没有办法在gunzip输出结果的过程中暂停吧?我想gunzip不会在读取一行输出之前就一直阻塞,所以一定有某个缓冲区在捕捉这些内容(无论是在Python解释器内部,还是在操作系统中,无论是在内存还是在磁盘上),这意味着解压后的文件一定是存储在某个地方的,对吧?
3 个回答
不是Python在暂停gunzip
,而是操作系统内核会在gunzip
尝试写入数据时(使用write()
这个系统调用)停止执行,因为它的缓冲区已经满了。这种情况叫做IO阻塞。内核会维护一个内部缓冲区,连接管道的两端,这个缓冲区和任何正在写入或读取管道的进程的缓冲区是独立的。
同样,当Python从一个空的管道读取数据时,也会出现阻塞的情况,也就是说,这个管道里没有gunzip
写入的数据。
管道可以被看作是解决生产者-消费者问题的一种方法。
这其实是来自于 gunzip
的实现,而不是来自 Python。它是用 C 语言写的。它可能使用 C 语言的 stdio.h
中的 fwrite()
来输出数据。
我使用的 libc6
实现会自动创建一个输出缓冲区,当这个缓冲区满了之后,就会在 fwrite()
这里停下来,等到可以写入更多数据时再继续。
你的想法有点问题。gunzip这个工具并不需要看到整个文件才能解压。你可以去看看解压文件的格式,里面有一个目录,记录了各个部分的位置。
其实可以分块解压一个文件。
“解压后的文件一定是完整存储在某个地方吧?”
不一定。其实不太明白你为什么会这么想,或者你是从哪里看到的。
所有底层的输入输出操作都有可能会被阻塞。gunzip在写入管道的时候,如果管道的缓冲区满了,就会被阻塞。这是管道输入输出的特性,管道的输入输出会阻塞。
想了解更多细节,可以查一下管道的手册。
如果一个进程试图从一个空的管道读取数据,那么read(2)会
被阻塞,直到有数据可用。如果一个进程试图写入一个
满的管道(见下文),那么write(2)会被阻塞,直到从管道中
读取到足够的数据,以便完成写入。通过使用fcntl(2)
的F_SETFL操作来启用O_NONBLOCK打开文件状态标志,可以实现非阻塞
输入输出。