【问题标题】:Why does the "yes | sleep 10" pipe not fail为什么“yes | sleep 10”管道不会失败
【发布时间】:2018-09-22 12:22:00
【问题描述】:

在考虑如何在我自己的一个程序中实现某个功能时,我一直想知道 bash 如何在内部处理以下性质的管道:

yes | sleep 10

这显然没有任何作用,但我不明白这不会导致错误。我会认为要么:

  • 因为sleep 不从标准输入读取,连接两个进程的管道会填满并导致yes 在尝试写入现在已满的管道时无限期阻塞

  • 如果使用非阻塞 IO,如果首先执行 yes 并在 sleep 进程甚至运行之前写入管道并且因此没有进程连接到管道

我想这是我的一些重大误解。我试过查看 bash 源代码,但那已经超出了我的想象。

【问题讨论】:

  • 在我的系统上我得到yes: standard output: Broken pipe,你不明白吗?如果你的意思是为什么$?等于0,那是因为你没有设置-o pipefail,见here。管道的退出状态是最右边命令的状态,sleep 10 返回成功。并且管道上的正确进程首先(及时)运行,因为首先需要一个进程在标准输入上监听,以便另一个进程开始发送。
  • 即使yes 被阻止写入一个完整的管道,为什么会导致错误?它将继续尝试直到管道消失,此时您将收到 Kamil Cuk 报告看到的错误。您希望看到什么错误?
  • 我没有收到那个错误(我在 Arch 上使用 bash 4.4.23)。我原以为即使在读取文件描述符关闭后写入完整管道也会阻塞,但我想可能不是这样,我想我可能会尝试一下。此外,我不太确定必须先运行正确的进程,在我的系统上,构成管道的命令并不总是从右到左启动。

标签: bash pipe


【解决方案1】:

这是运行 shell 命令yes | sleep 10 时实际发生的情况。

首先,shell 使用pipe system call 创建一个anonymous pipepipe 系统调用打开两个文件描述符,即管道的读取端和写入端。写入到写入端的任何内容都可以从读取端读取。

此后,shell 使用fork system call 创建两个子进程。两个孩子并行运行。

  • 在一个孩子中,shell 将管道的写入端连接到标准输出并关闭读取端。然后shell调用execve system call,用yes的代码镜像替换本进程中的代码镜像。
    程序yes 尽可能长时间地写入管道。如果在管道的读取端没有活动的read 调用,write 调用只会阻塞。 (实际上有一个小的缓冲区,write 在阻塞之前会填满,但这在这里无关紧要。)
  • 在另一个孩子中,shell 将管道的读取端连接到标准输入并关闭写入端。然后shell调用execve系统调用将本进程中的代码镜像替换为sleep的代码镜像。 程序sleep 10 秒内什么都不做。
  • 原始 shell 进程关闭管道的两端并等待其两个子进程退出(使用 wait system call)。

一旦 10 秒结束,运行 sleep 的进程就会退出。此时,管道的读取端不再在任何进程中打开。当一个进程试图写入一个读端未在任何进程中打开的管道时,内核会向写入进程发送一个SIGPIPE 信号。因此,运行yes 的进程被 SIGPIPE 信号杀死。

此时,shell 检测到其管道两端的子进程都已退出。管道命令返回右边的状态,即0(sleep退出成功)。

因为 sleep 不从标准输入读取,连接两个进程的管道会填满,并导致 yes 在尝试写入现在已满的管道时无限期阻塞

这是正确的。

如果使用非阻塞IO,如果先执行yes并在睡眠进程运行之前写入管道,则应该会发生错误,因此没有进程连接到管道的读取端

这在几个地方是不正确的。 yes 不使用非阻塞 IO。它与sleep 并行执行,而不是首先执行。永远不会有任何时间点没有进程连接到管道的读取端,直到sleep 退出。根据时间的不同,yes 可能会在sleep 开始执行之前开始写入,甚至可能在sleep 程序的子进程被分叉之前,但是当pipe 调用返回时读取端变为打开状态,在写端打开的同时。

【讨论】:

  • 那么当sleep 退出时yes 最终会收到EPIPE,而yes 是在阻塞写入整个管道的中间吗?因为yes 最终会终止。
  • 没有。 yes 在执行 write 系统调用时收到 SIGPIPE 信号。此系统调用不会返回,因为进程因信号而死亡。
  • @Peter 如果你忽略了SIGPIPE,你只会看到EPIPE
  • 啊,是的,SIGPIPE 就是我的意思。我想我现在明白了。
猜你喜欢
  • 2018-02-13
  • 1970-01-01
  • 2010-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-07
  • 1970-01-01
  • 2017-04-24
相关资源
最近更新 更多