在考虑如何在我自己的一个程序中实现某个功能时,我一直想知道bash如何在内部处理以下性质的管道:
yes | sleep 10
Run Code Online (Sandbox Code Playgroud)
这显然什么也没做,但我不明白这不会导致错误.我原以为:
因为sleep
没有从stdin读取,连接两个进程的管道将填满并导致yes
在它尝试写入现在的完整管道时无限期地阻塞
如果使用非阻塞IO,如果yes
先执行并在sleep
进程运行之前写入管道,则会发生错误,因此没有进程连接到管道的读取端
我想这是我的一些重大误解.我已经尝试过查看bash源代码,但这已经过去了.
Gil*_*il' 10
这是运行shell命令时实际发生的情况yes | sleep 10
.
首先,shell 使用系统调用创建一个匿名管道.该系统调用打开两个文件描述符,分别是读端和管道的写端.无论写入写入端的内容都可以从读取端读取.pipe
pipe
在此之后,shell使用fork
系统调用创建两个子进程.这两个孩子并行奔跑.
execve
系统调用,在此过程中用代码映像替换代码映像yes
.只要可以yes
写入管道.如果read
管道的读端没有活动呼叫,则write
呼叫会阻塞.(实际上有一个小缓冲区write
会在阻塞之前填满,但这在这里并不重要.)execve
系统调用,在此过程中用代码映像替换代码映像sleep
.该程序sleep
在10秒内无效.wait
系统调用).一旦10秒钟结束,流程就会sleep
退出.此时,管道的读取端在任何过程中都不再打开.当进程尝试写入读取端未在任何进程中打开的管道时,内核会向SIGPIPE
写入进程发送信号.因此,进程运行yes
被SIGPIPE信号杀死.
此时,shell检测到管道两侧的子进程已退出.pipeline命令返回右侧的状态,即0(sleep
成功退出).
因为sleep不从stdin读取,连接两个进程的管道将填满并导致yes在尝试写入现在的完整管道时无限期阻塞
这是对的.
如果使用非阻塞IO,如果首先执行yes并且在睡眠过程甚至运行之前写入管道,则应该发生错误,因此没有进程连接到管道的读取端
这在一些地方是不正确的.yes
不使用非阻塞IO.它与并行执行sleep
,而不是先执行.没有任何过程连接到管道的读取端,直到sleep
退出时,从来没有任何时间点.根据时间的不同,可能yes
会在sleep
开始执行之前开始写入,甚至可能在sleep
程序的子进程被分叉之前开始写入,但是当pipe
写入结束时,读取结束时读取结束时打开了读取结束.