用父进程交互式地操作子进程标准输入输出的问题。
我用下面的代码创建一个子进程,并用两个管道去重定向它的标准输入输出。子进程是一个交互式的命令行程序,其流程为:接受标准输入,运算,将结果送标准输出,然后继续接受输入,再远算输出,一直重复。父进程利用这两个管道来操作子进程的标准输入输出,其流程为写子进程标准输入管道,即让子进程接受标准输入,然后读子进程标准输出管道,接收子进程标准输入,然后再重复此过程。发现了一些有趣的问题:1,printf在重定向管道后不靠谱,比如,子进程的prinf内容不能被管道读取到,我猜想是存在buffer没有写到管道中的问题,在子进程中用fflush(stdout)后,管道可以读取到。2,在子进程中用c++的std::cout没有此问题,子进程的流程是 std::cin>>x, 再std::cout<<y,再std::cin>>x,std::cout<<y,依次重复,在第一次std::cout<<y后,它后面的std::cin>>x居然会在父进程还没开始写管道时就执行,只不过读出来的数据是空,这与子进程单独运行不一样,单独运行时,在第二次std::cin>>x时会阻塞在那等待标准输入,而在标准输入被重定向到管道后,它在第一次std::cin>>x时已经读取了管道内容了,在第二次std::cin>>x时,它没有等待父进程写管道,而是直接就读取空串返回了,在第一次之后的std::cin>>x之前加上fflush(stdin)后,就没有此问题了。我试验在父进程中来做flush管道的操作,都不能解决此问题。
我想与大家讨论的是,如果我想写一个父进程来交互式地控制子进程的标准输入输出,有没有办法只在父进程的代码上做文章,使父进程能顺利地与子进程交互,不管子进程是用scanf, printf, cin, cout还是其它的做标准输入输出的方法。毕竟对于有些已经存在的程序,它没有用cin, cout,也没有在cin前做fflush(stdin)的操作?
另外顺便问下大家,我在VS2003中加了两个工程,一个子进程,一个父进程,有没有方法让我可以同时调试这两个进程,比如我将子进程工程设置为父进程工程的reference,父进程设为启动工程,在子进程的代码处打上断点,然后运行就可以在子进程代码的断点处停下。如果是dll,这个完全没有问题,也许进程就不行,不晓得大家知道有方法这样做没?我记得注册表中好像有个目录,在里面加上子进程的可执行文件名,就可以设置其启动,调试参数,如果将它设置为在VS2003中启动,也许就可以调试它了,不知这样是否可行?
文字能力不强,篇幅多了,希望大家见谅。
启动子进程代码:
PROCESS_INFORMATION pi;
bool BridgeStart(char* target_name)
{
BOOL bSuccess; /* BOOL return code for APIs */
SECURITY_ATTRIBUTES sa;
STARTUPINFO si;
/* set up the security attributes for the anonymous pipe */
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
// create anonymous pipe
bSuccess = CreatePipe(&hHostRead, &hSlaveWrite, &sa, 0);
//PERR(bSuccess, "CreatePipe");
// create anonymous pipe
bSuccess = CreatePipe(&hSlaveRead, &hHostWrite, &sa, 0);
//PERR(bSuccess, "CreatePipe");
/* Set up the STARTUPINFO structure for the CreateProcess() call */
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = hSlaveRead;
si.hStdOutput = hSlaveWrite;
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
/* Set up the PROCESS_INFORMATION structure for the CreateProcess() call */
ZeroMemory( &pi, sizeof(pi) );
// Start the child process.
bSuccess = CreateProcess(NULL, // No module name (use command line)
LPTSTR(target_name), // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
TRUE, // Set handle inheritance to TRUE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi); // Pointer to PROCESS_INFORMATION structure
//PERR(bSuccess, "CreateProcess");
if (bSuccess)
return true;
else
return false;
}
读写子进程输入输出管道代码:
int BridgeRead(char* buf, int len)
{
BOOL bSuccess; /* BOOL return code for APIs */
DWORD n; /* number of bytes read or to be written */
bSuccess = ReadFile(hHostRead, /* read handle */
buf, /* buffer for incoming data */
len, /* number of bytes to read */
&n, /* number of bytes actually read */
NULL); /* no overlapped reading */
if (!bSuccess && (GetLastError() == ERROR_BROKEN_PIPE))
return(-1);
else
return(n);
}
int BridgeWrite(char* buf, int len)
{
BOOL bSuccess; /* BOOL return code for APIs */
DWORD n; /* number of bytes read or to be written */
bSuccess = WriteFile(hHostWrite,/* write handle */
buf, /* buffer to write */
len, /* number of bytes to write */
&n, /* number of bytes actually written */
NULL); /* no overlapped writing */
if (!bSuccess && (GetLastError() == ERROR_BROKEN_PIPE))
return(-1);
else
return(n);
}
子进程代码
int _tmain(int argc, _TCHAR* argv[])
{
int a,b;
while(1)
{
fflush(stdin);//如果此行去掉,重定向输入输出到管道后就会出问题。
std::cin >> a >> b;
std::cout << a + b << std::endl;
}
return 0;
}