In thinking about how to implement a certain feature in one of my own programs I've been wondering how bash handles pipes of the following nature internally:
yes | sleep 10
This obviously does nothing but I don't understand how this does not result in an error. I would have thought that either:
because sleep
does not read from stdin, the pipe connecting both processes would fill up and cause yes
to block indefinitely when it attempts to write to the now full pipe
if non-blocking IO is used, errors should occur if yes
is executed first and writes to the pipe before the sleep
process is even run and thus no process is connected to the read end of the pipe
I guess this is some major misunderstanding on my part. I've tried looking at the bash source code but that's gone over my head.
Here's what actually happens when you run the shell command yes | sleep 10
.
First the shell creates an anonymous pipe using the pipe
system call. The pipe
system call opens two file descriptors which are the read end and the write end of the pipe. Whatever gets written to the write end becomes available for reading from the read end.
After this, the shell creates two child processes using the fork
system call. The two children run in parallel.
execve
system call to replace the code image in this process by the code image of yes
.yes
writes to the pipe as long as it can. If there isn't an active read
call on the read end of the pipe, the write
call just blocks. (There's actually a small buffer which write
will fill up before blocking, but this doesn't matter here.)execve
system call to replace the code image in this process by the code image of sleep
.
The program sleep
does nothing for 10 seconds.wait
system call).Once the 10 seconds are up, the process running sleep
exits. At this point, the read end of the pipe is no longer open in any process. When a process tries to write to a pipe whose read end is not open in any process, the kernel sends a SIGPIPE
signal to the writing process. Thus the process running yes
is killed by the SIGPIPE signal.
At this point, the shell detects that its child processes on both sides of the pipeline have exited. The pipeline command returns the status of the right-hand side, which is 0 (sleep
exits successfully).
because sleep does not read from stdin, the pipe connecting both processes would fill up and cause yes to block indefinitely when it attempts to write to the now full pipe
This is correct.
if non-blocking IO is used, errors should occur if yes is executed first and writes to the pipe before the sleep process is even run and thus no process is connected to the read end of the pipe
This is not correct in several places. yes
does not use non-blocking IO. It's executed in parallel with sleep
, not first. There is never any point in time when no process is connected to the read end of the pipe, not until sleep
exits. Depending on the timing, it's possible that yes
will start writing before sleep
starts executing, maybe even before the child process for the sleep
program is forked, but the read end became open when the pipe
call returned, at the same time as the write end became open.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With