如何在Python子进程中处理巨量输入的管道`head`?

0 投票
1 回答
766 浏览
提问于 2025-04-18 08:43

在Linux中,如果一系列命令通过管道连接在一起,系统会很高效地处理这些命令。也就是说,当最后一个子进程结束时,之前的子进程会被终止。例如:

cat filename | head -n 1
zcat filename | head -n 1
hadoop fs -cat /some/path | head -n 1

在上面的例子中,cat命令可能会花费很多时间,但组合后的命令执行得很快。这是怎么做到的呢?当head命令结束时,操作系统会立即给第一个命令(cat命令)发送SIGTERM或SIGKILL信号吗?

我想在Python中做类似的事情,想知道最好的方法是什么。我正在尝试做以下操作:

p1 = Popen(['hadoop','fs','-cat',path], stdout=PIPE)
p2 = Popen(['head','-n',str(num_lines)], stdin=p1.stdout,stdout=PIPE)
p2.communicate()
p1.kill() or p1.terminate()

这样做有效吗?

相关问题:

1 个回答

1

其实,我觉得当头部(head)关闭时,进程会收到一个叫做 SIGPIPE 的信号。根据维基百科的解释:

SIGPIPE

当一个进程试图向一个没有连接到另一端的管道写入数据时,就会向这个进程发送 SIGPIPE 信号。

此外,还有一些来自关于SIGPIPE的问题的回答:

...

你看,当那个有待写入的文件描述符被关闭时,SIGPIPE 就会立刻发生。虽然写入最终会返回 -1,但这个信号的主要作用是异步通知你,写入已经不再可能。这是让UNIX中管道优雅协作结构正常工作的一个部分。

...

https://stackoverflow.com/a/8369516/2334407


...

https://www.gnu.org/software/libc/manual/html_mono/libc.html

这个链接说:

一个管道或FIFO必须同时在两端打开。如果你从一个没有任何进程在写入的管道或FIFO文件中读取(可能是因为所有进程都关闭了文件,或者退出了),读取会返回文件结束(end-of-file)。向一个没有读取进程的管道或FIFO写入数据会被视为错误;这会生成一个 SIGPIPE 信号,如果这个信号被处理或阻塞,写入会失败并返回错误代码 EPIPE。

...

https://stackoverflow.com/a/18971899/2334407


我认为这样做是为了在处理错误时不需要在每个写入管道的地方写很多代码。

有些程序会忽略 write() 的返回值;如果没有 SIGPIPE,它们会无用地生成所有输出。

那些检查 write() 返回值的程序,如果写入失败,可能会打印错误信息;但对于一个断开的管道来说,这并不算真正的错误,因为整个管道并没有出问题。

https://stackoverflow.com/a/8370870/2334407


现在,关于你问的最佳做法,我会说不要发送任何信号。相反,读取你需要的数据,然后简单地关闭管道。操作系统内核会自动为你清理,并向必要的进程发送 SIGPIPE 信号。

撰写回答