Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python close_fds not clear

I had an issue with close_fds in Python27 so after doing some research I found this example:

from subprocess import Popen, PIPE, STDOUT
p1 = Popen(['cat'], stdin=PIPE, stdout=PIPE)
p2 = Popen(['grep', 'a'], stdin=p1.stdout, stdout=PIPE)
p1.stdin.write("aaaaaaaaaaaaaaaa\n")
p1.stdin.close()
p2.stdout.read()

My problem is that I can't understand why p1.stdin remains open. p1 is not a child of p2 so p2 shouldn't inherit any p1 resource except p1.stdout which is explicitly passed. Furthermore why setting close_fds=True in p2 resolves the issue? Here is written this:

If close_fds is true, all file descriptors except 0, 1 and 2 will be closed before the child process is executed.

So even if I will be able to understand the inheritance between p1 and p2 still p1.stdin shouldn't be closed by close_fds=True because it is the standard input (1).

like image 834
Luigi Tiburzi Avatar asked Nov 13 '13 09:11

Luigi Tiburzi


1 Answers

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().)

like image 64
user4815162342 Avatar answered Nov 01 '22 17:11

user4815162342