Since p1
and p2
are siblings, there is no inheritance going on between their corresponding processes directly.
However, consider the file descriptor that the parent sees as p1.stdin
, inherited by p1
and redirected to its stdin
. This file descriptor exists in the parent process (with a number other than 0, 1, or 2 - you can verify this by printing p1.stdin.fileno()
), and it has to exist, because we intend to write to it from the parent. It is this file descriptor that is unintentionally inherited and kept open by p2
.
When an open file is referenced by multiple file descriptors, as is the case with p1.stdin
, it is only closed when all the descriptors are closed. This is why it is necessary to both close p1.stdin
and pass close_fds
to p2
. (If you implemented the spawning code manually, you would simply close the file descriptor after the second fork()
.)