为何子进程关闭stdout句柄后仍能写入?——subprocess代码疑问
p1.stdout Doesn’t Break p1’s Output Great question—this is one of those tricky Unix pipe details that confuses even experienced developers at first. Let’s break down exactly what’s happening here.
First, let’s recap the code we’re looking at:
p1 = Popen(["dmesg"], stdout=PIPE) p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits. output = p2.communicate()[0]
What stdout=PIPE Actually Does
When you create p1 with stdout=PIPE, the subprocess module sets up a Unix pipe behind the scenes. Here’s the breakdown:
- The pipe has two ends: a write end and a read end.
p1(thedmesgprocess) has its standard output connected to the pipe’s write end. It writes all its output here.- Your Python parent process gets a file handle (
p1.stdout) pointing to the pipe’s read end. This is how your script would normally readp1’s output directly.
Why Closing p1.stdout Doesn’t Stop p1
The key thing to remember is: each process has its own independent set of file descriptors. The p1.stdout handle in your Python script belongs to your parent process, not to the dmesg child process.
When you call p1.stdout.close(), you’re only closing the parent process’s reference to the pipe’s read end. The dmesg process still has its write end open, and p2 (the grep process) still has its own reference to the pipe’s read end (since we passed stdin=p1.stdout to it).
So dmesg can keep writing to the pipe, and grep can keep reading from it—your parent process just isn’t holding onto that read end anymore.
The Real Reason for p1.stdout.close()
That comment about allowing p1 to receive a SIGPIPE is the crucial part. Here’s why that matters:
- Unix pipes send a
SIGPIPEsignal to a process trying to write to a pipe when all read ends of the pipe are closed. - If you didn’t close
p1.stdoutin the parent process, then even ifp2(grep) crashes or exits early, your parent process would still be holding onto the pipe’s read end. That means the pipe isn’t fully closed, sop1would never get the SIGPIPE signal—it would just hang forever, waiting for someone to read its output. - By closing
p1.stdout, you ensure that the only read end of the pipe is the one held byp2. Ifp2exits, all read ends are gone, andp1gets a SIGPIPE (which by default terminates the process cleanly) instead of hanging.
Your Experiment Makes Sense Now
When you tested this, you probably saw p1 keep running and outputting to p2—that’s exactly what should happen! The only time closing p1.stdout affects p1 is if p2 dies first. In normal operation (where p2 runs to completion), p1 writes all its output to the pipe, p2 reads it, and everything works as expected.
内容的提问来源于stack exchange,提问作者Aran-Fey

