如果管道已满,写入管道的进程是不是会阻塞?
Posted
技术标签:
【中文标题】如果管道已满,写入管道的进程是不是会阻塞?【英文标题】:Will a process writing to a pipe block if the pipe is full?如果管道已满,写入管道的进程是否会阻塞? 【发布时间】:2015-04-24 15:53:17 【问题描述】:我目前正在深入研究 Win32 API,并为CreateProcess
和CreatePipe
编写一个包装类。我只是想知道如果我打开的进程写入了太多输出以使管道缓冲区无法容纳会发生什么。该过程会等到我从管道的另一端读取数据吗? CreatePipe
函数的备注表明:
当进程使用 WriteFile 写入匿名管道时,直到写入所有字节后,写入操作才会完成。如果在写入所有字节之前管道缓冲区已满,则 WriteFile 不会返回,直到另一个进程或线程使用 ReadFile 来提供更多可用缓冲区空间。
https://msdn.microsoft.com/en-us/library/windows/desktop/aa365152%28v=vs.85%29.aspx
假设我使用CreateProcess
打开一个进程,然后使用WaitForSingleObject
等待进程退出。如果进程超出其标准输出管道的缓冲区大小,它会退出吗?
【问题讨论】:
如果官方引用说WriteFile
函数“直到...才返回”,这与它阻塞相同。那么答案是肯定的写入一个完整的管道会阻塞。顺便说一句,这是标准行为,例如POSIX 系统(如 Linux 或 OSX)也是如此。
在 POSIX 系统中确实会发生这种情况:生产者被阻塞,直到有空间可以写入。例如,当您执行ls -R / | less
时,less
将读取一些文本屏幕,然后阻止ls
,直到用户向下滚动。如果用户杀死阅读器进程(例如通过退出less
),原始进程将被杀死。这样可以节省很多无用的计算。
“不返回” == 块。如果您不阅读重定向的输出,那么,是的,该过程很可能会死锁。一个非常标准的错误是耗尽标准输出而不是标准错误。您需要在所有 3 个句柄上使用 WaitForMultipleObjects()。
@HansPassant 如果进程退出,输出不会被关闭,所以你可以只读取输出管道而不包括进程?你能把 stderr 留在控制台上,这样就不是你的问题了吗?
@Random832:当然,您可以将stderr
绑定到控制台流。或者 stdout
和 stderr
到同一个管道。
【参考方案1】:
WaitForSingleObject
在具有重定向输出的进程上确实是一个死锁。您需要保持输出管道排空,以便让子进程运行完成。
通常您会在管道上使用重叠 I/O,然后在句柄对1(进程句柄,管道读取事件句柄)上使用 WaitForMultipleObjects
,直到进程句柄发出信号为止。
Raymond Chen 写了关于输入也通过管道传输的场景:
Be careful when redirecting both a process's stdin and stdout to pipes, for you can easily deadlock1 正如 Hans 所说,可以有多个输出流。 stdout
、stderr
是典型的,通过句柄继承可以实现更多。排空所有从流程中出来的管道。
【讨论】:
感谢您提供翔实的回答。我还有一个问题:当我在为该进程创建的所有管道上调用CloseHandle
时,死锁是否还会发生,或者我可以安全地调用WaitForSingleObject
?我想添加一个Wait()
方法,它会简单地忽略进程产生的任何输出,但会阻塞直到它终止。
你问的是CloseHandle是父进程还是子进程?这将防止死锁,但管道的另一端在尝试写入或读取零字节时会遇到错误。
在父进程中。使用“子进程中的句柄”,您指的是我在STARTUPINFO
结构中指定的句柄吗?假设子进程从标准输入中读取数据,而我从父进程中关闭它,那么子进程会遇到错误还是只会在标准输入管道中读取任何内容(或剩下的内容)?
@NiklasR:如果管道的写入端关闭,读取端将开始返回零长度读取。这应该被识别为“文件结束”条件。以上是关于如果管道已满,写入管道的进程是不是会阻塞?的主要内容,如果未能解决你的问题,请参考以下文章