如果缓冲区溢出,shell 管道是否会阻塞上游源进程?

如果缓冲区溢出,shell 管道是否会阻塞上游源进程?

我执行各种系统管理任务来清理我的磁盘,例如(但不限于):

find /media/me/disk_with_huge_inode_count -type d -empty | xargs rmdir -p

并且该rmdir部分确实很慢,但find相比之下却产生了大量的输出。

find在这种情况下,行为会是怎样的?

我并不是想寻求针对此操作的具体建议,因为我对其他类似作业也有同样的担忧。我想了解的是当生产者和消费者负载不匹配时,Linux 内核(或 shell?)如何处理管道溢出。

答案1

具体案例

是的,find只要有必要就会阻塞。“简单”测试如下:

find / -print -exec sh -c 'printf "%s\\n" "$1" >/dev/tty' find-sh {} \; \
| while </dev/tty read user_input; do read from_find; done

find -print是管道的路径名(find在您的代码中执行相同的操作,它使用隐式-print)。每个路径名都会被额外打印到/dev/tty,因此您会看到它 -print成功。在某个时刻,您看到的输出将阻塞,这是当管道缓冲区(几乎)已满时。按下Enter以触发read from_find从管道读取并在缓冲区中腾出空间。

很可能你需要按几次Enter(在实践中,最好按住并保持耐心),直到find打印出另一组路径名/dev/tty。不过,不按也Enter可以find阻止任意地长的。


一般情况

你写了:

我想了解 Linux 内核(或 shell?)如何处理管道溢出 […]

shell 负责进行设置:创建具有描述符(包括标准描述符:stdin、stdout、stderr)的进程,并将其连接到相应的文件(未命名的 fifos,即管道;或其他类型)。在进程之间通过管道传输的数据(例如findxargs)不会通过 shell 传输。shell 不充当中继。

要了解如何处理管道溢出,通常可以从以下片段中获得一些见解POSIX 规范write()

描述

[…]

对管道或 FIFO 的写入请求应以与常规文件相同的方式处理,但有以下例外:

[…]

  • 如果O_NONBLOCK标志清除,则写入请求可能会导致线程阻塞,但正常完成时它将返回nbyte

  • 如果O_NONBLOCK设置了该标志,则write()请求将按照以下方式进行不同的处理:

    • write()函数不得阻塞线程。

    […]

[…]

错误

[…]

[EAGAIN]
该文件是管道或FIFO,O_NONBLOCK文件描述符设置了标志,并且线程将在写入操作中延迟。

[…]

理由

[…]

尝试写入管道或 FIFO 有几个主要特征:

[…]

  • 阻止/立即:阻塞只有在清除的情况下才有可能O_NONBLOCK。如果有足够的空间让所有请求的数据立即写入,实现应该这样做。否则,调用线程可能会阻塞;也就是说,暂停,直到有足够的空间可供写入。[…]

[…]

这意味着写作线程可以:

  • 设置O_NONBLOCK,当缓冲区空间不足时 [EAGAIN],它将能够继续(执行其他任务)并最终尝试再次写入;或者
  • 清除O_NONBLOCK,如果缓冲区空间不足,它就会阻塞。

如果程序的线程使用write()with O_NONBLOCKset,程序将跟踪哪些内容已成功写入,哪些内容需要再次尝试。使用O_NONBLOCKclear,管道缓冲区已满就不再是问题:线程只需使用,write()并根据需要阻塞;这种阻塞发生在write()不需要额外工作(代码)的情况下,例如轮询、陷阱或任何其他操作。

read()可以阻止类似情况write(),情况比较类似,我就不单独阐述了。

这种设计使程序能够可靠且轻松地使用管道。管道的整个理念是写入者在各自的缓冲区中等待空间,读取者在各自的缓冲区中等待某些东西;因此,即使存在瓶颈,数据最终也会流过。

可以编写一个不耐烦的程序,如果无法(几乎)立即写入(或读取,如果是读取器)则退出。设计用于管道的程序应具有无限的耐心。标准 *nix 工具(包括find)在设计上具有无限的耐心¹。构建不耐烦的程序或将标准耐心工具包装在实现超时的程序中需要额外的努力。

如果“管道”是循环的,则可能会发生死锁(例子)或者如果它稍后分支并收敛(如这里)。这是一个单独的问题,与程序处理数据的速度几乎没有关系(如果有的话)。它不会发生在管道的线性排列中。

write()我们考虑了和的 POSIX 规范read()Linux 被归类为“基本符合 POSIX 标准”。它不是“完全兼容”,但我不认为它在相关领域会与 POSIX 有太大偏差。可靠工作的管道太重要了。

¹ 我不期望实现能够阻止到宇宙的尽头,或者超过一年2038 或 2147485547我说的“无限耐心程序”是指程序本身不会故意不耐心。


结论

您的命令作为管道很好。(由于其他原因,它存在缺陷,请参见下文。)


边注

find … | xargs rmdir -p路径名包含空格(如空格)、换行符、单引号或双引号、反斜杠时,您的路径名将出现错误。这是因为xargs没有特定的选项解释这些。

一种可靠的方法是 byfind … -print0 | xargs -0 …或 by find … -exec …。后者是便携式的。

find /media/me/disk_with_huge_inode_count -type d -empty -exec rmdir -p {} +

(据我所知,等待完成find时也是无限耐心的。)-exec

相关内容