我执行各种系统管理员任务来清理我的磁盘,例如(但不限于):
find /media/me/disk_with_huge_inode_count -type d -empty | xargs rmdir -p
而且这rmdir
部分真的很慢,find
相比之下产生了大量的输出。
find
在这种情况下会有什么行为?
我不是在寻找有关此操作的具体建议,因为我对其他类似工作也有这种担忧。我想了解的是,当生产者和消费者负载不匹配时,Linux 内核(或 shell?)如何处理管道溢出。
具体案例
是的,
find
只要有必要就会阻止。一个“简单”的测试就像:这
find
-print
是管道的路径名(find
在您的代码中也是如此,它使用隐式-print
)。每个路径名都附加打印到,因此您在成功后/dev/tty
会看到它。在某些时候,您看到的输出会阻塞,这是管道缓冲区(几乎)已满的时候。按下以触发从管道读取并在缓冲区中腾出空间。-print
Enterread from_find
很可能您将需要多次按下Enter(实际上最好握住它并耐心等待),直到将
find
另一束路径名打印到/dev/tty
. 然而,通过不按Enter,您可以任意长时间find
阻止。一般情况
你写了:
shell 负责设置:使用连接到各个文件(未命名的fifos,即管道;或其他类型的文件)的描述符(包括标准描述符:stdin、stdout、stderr)创建进程。流经进程之间管道的数据(如您的
find
andxargs
)不会流经外壳。外壳不充当中继。要了解如何处理管道溢出,通常您可以从以下POSIX 规范的
write()
片段中获得一些见解:这意味着写入线程可以:
O_NONBLOCK
并且在缓冲区中没有足够空间的情况下它将获得[EAGAIN]
,能够继续(与其他任务)并最终尝试再次写入;或者O_NONBLOCK
,如果缓冲区中没有足够的空间,它将阻塞。如果一个程序的线程使用
write()
了O_NONBLOCK
set,程序将跟踪哪些已经写成功,哪些需要再次尝试。使用O_NONBLOCK
clear 时,管道缓冲区已满不是问题:线程只是使用write()
它,它会在必要时被阻塞;这种阻塞发生在write()
并且不需要额外的努力(代码),如轮询、陷阱或任何东西。read()
可以block likewrite()
,情况大同小异,我不再赘述。这种设计允许程序可靠且轻松地使用管道。管道的整个想法是写入者在各自的缓冲区中等待空间,阅读者在各自的缓冲区中等待某些东西;因此,即使存在瓶颈,数据最终也会流过。
可以编写一个不耐烦的程序,如果它无法(几乎)立即写入(或读取,如果是读者),它将退出。设计用于管道的程序应具有无限的耐心。标准 *nix 工具(包括
find
)在设计上具有无限的耐心¹。构建一个不耐烦的程序或将标准的耐心工具包装在实现超时的东西中需要额外的努力。如果“管道”是循环的(示例)或者如果它稍后分支并收敛(如这里),则可能发生死锁。这是一个单独的问题,与程序处理数据的速度几乎没有关系(如果有的话)。它不会发生在管道的线性排列中。
write()
我们考虑了和的 POSIX 规范read()
。Linux 被归类为“大部分符合 POSIX 标准”。它不是“完全合规的”,但我不希望它在相关领域显着偏离 POSIX。可靠工作的管道太重要了。¹我不希望实现能够阻止到宇宙的尽头,或者超过2038 年或 2147485547年。我所说的“无限耐心程序”是指一个本身并不是故意不耐烦的程序。
结论
您的命令作为管道很好。(由于另一个原因,它存在缺陷,请参见下文。)
边注
find … | xargs rmdir -p
对于包含空格(如空格)、换行符、单引号或双引号、反斜杠的路径名,您将表现不佳。这是因为xargs
没有特定选项会解释这些。一个可靠的方法是 by
find … -print0 | xargs -0 …
或 byfind … -exec …
。后者是便携式的。(AFAIK
find
在等待-exec
完成时也非常耐心。)