C++ 如何正确读取子进程的stdout/stderr输出?
我已经编写了一个程序C++ 如何正确读取子进程的stdout/stderr输出?,c++,windows,winapi,process,C++,Windows,Winapi,Process,我已经编写了一个程序a.exe,它使用函数启动我编写的另一个程序b.exe。调用者创建两个管道,并将两个管道的写入端作为stdout/stderr句柄传递给CreateProcess,以用于子进程。这实际上与MSDN上的样本相同 由于它似乎无法使用一个同步调用来等待进程退出或stdout或stderr上的数据可用(该函数在管道上不工作),因此调用方有两个线程正在运行,这两个线程都在stdout/stderr管道的读取端执行(阻塞)调用;下面是用于stdout/stderr的“read threa
a.exe
,它使用函数启动我编写的另一个程序b.exe
。调用者创建两个管道,并将两个管道的写入端作为stdout/stderr句柄传递给CreateProcess,以用于子进程。这实际上与MSDN上的样本相同
由于它似乎无法使用一个同步调用来等待进程退出或stdout或stderr上的数据可用(该函数在管道上不工作),因此调用方有两个线程正在运行,这两个线程都在stdout/stderr管道的读取端执行(阻塞)调用;下面是用于stdout/stderr的“read thread procedure”的确切代码(我不是自己写这段代码的,我想是有同事写的):
a.exe
然后使用一个简单的调用等待b.exe
终止。调用返回后,两个读取线程终止(因为管道断开),并使用关闭两个管道的读取端
现在,我遇到的问题是:b.exe
可能(取决于用户输入)启动比b.exe
本身寿命更长的外部进程,基本上是守护进程。在这种情况下,stdout/stderr管道的写入端被继承到该守护进程,因此管道永远不会中断。这意味着a.exe
中的WaitForSingleObject调用将返回(因为b.exe
已完成),但其中一个管道上的CloseHandle调用将被阻塞,因为两个读取线程仍处于其(阻塞!)ReadFile调用中
在返回b.exe
后,如何在不使用蛮力()终止两个读取线程的情况下解决此问题?如果可能,我希望避免任何涉及管道轮询和/或流程轮询的解决方案
更新:以下是我迄今为止尝试的内容:
b.exe
继承a.exe
;这不管用。MSDN特别指出传递给CreateProcess的句柄必须是可继承的b.exe
:似乎没有任何效果(如果有,我会感到惊讶)或
守护进程应该关闭其继承的文件描述符。似乎在Windows Vista之前的Windows版本上(您可以使用该功能的地方),无法使用终止读取线程
一个合适的选择()可能是使用异步调用,但在我的情况下这是不可能的(需要对现有代码进行太多更改)。设置一些全局标志(bool exit_标志)并在a.exe中向管道写入内容也许可以尝试CancelSynchronousIO?@adf88:根据
WaitForMultipleObjects
上的MSDN页面,它可以处理所有类型的事情,但不能处理管道。我的实验似乎证实了这一点:管道的读取端总是有信号,即使没有可用数据。CancelSynchronousIO()函数看起来不错,但它不是我的选项,因为它只在Windows Vista和更高版本上可用。可能重复:@adf88:你提到的问题看起来类似,但它不是这个问题的重复。我面临着同样的症状,但我要在比其他问题更广阔的解决空间中寻求解决方案。应该是这样的足够b.exe
将FALSE
传递给CreateProcess()的bInheritHandles
参数.1:这是一个有趣的观点;值得一提的是,b.exe
启动的守护程序是一个客户的程序。然而,在责怪他之前,我想有更多的论据来支持这一说法。你是否碰巧有一些解释的链接,或者你可以稍微扩展一下你的回答?成为守护程序意味着什么s进行一些操作(“守护进程化”)并关闭继承的文件描述符就是其中之一(在UNIX上,它们通常从/重定向到/dev/null
,而不是关闭)。我相信这在每本关于UNIX系统编程的书中都有教过(是的,我记得你关心windows,但守护进程的概念最初来自UNIX)。(由于长度限制而拆分注释)对于在线参考,您可以使用Wikipedia文章及其包含的引用。您可以在谷歌上搜索许多编写守护进程的方法,如:(第4.6节“关闭标准文件描述符”)顺便说一句,如果你不想打扰你的客户,你可以自己关闭文件描述符。在UNIX上这看起来像:fork(创建当前进程的副本);在子进程中:关闭文件描述符(或重定向);exec“b.exe”(在当前(子)进程中执行b.exe的代码)。由于windows缺少fork/exec调用,因此可能存在类似于此操作序列的操作。windows有一个setHandle信息((HANDLE)sockfd,HANDLE\u FLAG\u INHERIT,0)调用,您可以使用它来确保特定套接字不会被生成的子进程继承。不过,我认为b.exe的作者将需要调用它。+1:解析管道输出,寻找“进程结束”标记,这是一个创造性的想法。;-)
DWORD __stdcall ReadDataProc( void *handle )
{
char buf[ 1024 ];
DWORD nread;
while ( ReadFile( (HANDLE)handle, buf, sizeof( buf ), &nread, NULL ) &&
GetLastError() != ERROR_BROKEN_PIPE ) {
if ( nread > 0 ) {
fwrite( buf, nread, 1, stdout );
}
}
fflush( stdout );
return 0;
}