使用 ReadFile 和 WriteFile 时出现死锁

Posted

技术标签:

【中文标题】使用 ReadFile 和 WriteFile 时出现死锁【英文标题】:Deadlock while using ReadFile and WriteFile 【发布时间】:2018-12-09 18:48:51 【问题描述】:

我正在做一个学校项目,我试图在子进程的 StdOut 中显示父进程的 StdIn 中写入的内容;反之亦然,即在父 StdOut 上显示子进程 StdIn 上写入的内容,但我在 ReadFile 和 WriteFile 操作时遇到了死锁。

根据我在研究该主题期间收集到的信息,这是使用同步管道时常见的问题。

管道操作的读写是否应该通过事件同步? 你建议其他方法吗? 任何建议将不胜感激,在此先感谢。

Parent.cpp

   #include <windows.h>
   #include <iostream>
   #include <stdio.h>

   //read handle pipe1
   HANDLE r1 = NULL;

   //write handle pip1
   HANDLE w1 = NULL;

   //read handle pipe2
   HANDLE r2 = NULL;

   //write handle for pipe2
   HANDLE w2 = NULL;

   #define BUFSIZE 4096

   void CreateChildProcess() 
       TCHAR applicationName[] = TEXT("Child");
       PROCESS_INFORMATION pi;
       STARTUPINFO si;
       BOOL success = FALSE;

       ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
       ZeroMemory(&si, sizeof(STARTUPINFO));

       si.cb = sizeof(STARTUPINFO);
       si.hStdError = w1;
       si.hStdOutput = w1;
       si.hStdInput = r2;
       si.dwFlags |= STARTF_USESTDHANDLES;


       success = CreateProcess(NULL, applicationName, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);

       if (!success) 
           printf("Error creating child process \n");
       
       else 

           printf("Child process successfuly created \n");
           CloseHandle(pi.hProcess);
           CloseHandle(pi.hThread);
       
   
   void WriteToPipe() 
       DWORD read, written;
       CHAR chBuf[BUFSIZE];
       BOOL success = FALSE;

       HANDLE pStdIn = GetStdHandle(STD_INPUT_HANDLE);

       for (;;)
       
           success = ReadFile(pStdIn, chBuf, BUFSIZE, &read, NULL);
           if (!success || read == 0) break;

           success = WriteFile(w2, chBuf, read, &written, NULL);
           if (!success) break;
       
   

   void ReadFromPipe() 
       DWORD read, written;
       CHAR chBuf[BUFSIZE];
       BOOL success = FALSE;

       HANDLE pStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

       for (;;)
       
           success = ReadFile(r1, chBuf, BUFSIZE, &read, NULL);
           if (!success || read == 0) break;

           success = WriteFile(pStdOut, chBuf, read, &written, NULL);
           if (!success) break;
       
   

   int main()
   

       DWORD dRead, dWritten;
       CHAR chBuf[BUFSIZE];
       BOOL bSuccess = FALSE;

       SECURITY_ATTRIBUTES secAttr;
       secAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
       secAttr.bInheritHandle = TRUE;
       secAttr.lpSecurityDescriptor = NULL;

       printf("Creating first pipe\n");
       if (!CreatePipe(&r1, &w1, &secAttr, 0)) 
           printf("\nError creating first pipe\n");
       
       printf("Creating second pipe\n");
       if (!CreatePipe(&r2, &w2, &secAttr, 0)) 
           printf("Error creating second pipe \n");
       


       if (!SetHandleInformation(r1, HANDLE_FLAG_INHERIT, 0)) 
           printf("r1 SetHandleInformation \n");
       
       if (!SetHandleInformation(w2, HANDLE_FLAG_INHERIT, 0)) 
           printf("w2 SetHandleInformation \n");
       


       printf("\nCreating child process..... \n");
       CreateChildProcess();

       WriteToPipe();
       ReadFromPipe();

       return 0;
   

Child.cpp

   #include <windows.h>
   #include <stdio.h>
   #include "pch.h"

   #define BUFSIZE 4096

   int main()
   
       DWORD dRead, dWritten;
       CHAR chBuf[BUFSIZE];
       BOOL success = FALSE;
       HANDLE stdIn = GetStdHandle(STD_INPUT_HANDLE);
       HANDLE stdOut = GetStdHandle(STD_OUTPUT_HANDLE);


       if (stdIn == INVALID_HANDLE_VALUE || stdOut == INVALID_HANDLE_VALUE) 
           ExitProcess(1);
       

       for (;;) 
           success = ReadFile(stdIn, chBuf, BUFSIZE, &dRead, NULL);
           if (!success || dRead == 0) break;
success = WriteFile(stdOut, chBuf, dRead, &dWritten, NULL);
           if (!success) break;
       

       return 0;
   

【问题讨论】:

您的流程究竟在什么时候停止?你观察到了什么?为什么你认为这是一个僵局? 您永远不会关闭管道句柄。父进程似乎也无缘无故地通过标准输入输出读写。 当然你在这里遇到了死锁,因为使用同步 io 并且两个进程都从 ReadFile 开始。使用异步 io 并且只有 single 管道对。 按下调试器中的“break”按钮并检查调用堆栈以查看代码中发生死锁的位置。 @zett42 - 在具体情况下,这很明显 - 在调用 ReadFile 中 - 此同步调用(如果所有参数都正确)不会返回,直到数据不会写入管道。但两个进程都从ReadFile开始。 【参考方案1】:

您将无法在父 StdOut 上显示子进程 StdIn 上写入的内容,因为子进程的 stdIn 句柄设置为“r2”。这样您就无法手动在子进程中插入任何内容。我建议使用namepipe,这样孩子的std句柄就不会和其他管道句柄混在一起了。

for(;;) 等于while(1),而ReadFile() 直到接收到数据才会返回,所以如果没有异常(排除没有数据传入)发生,循环永远不会中断,并且代码不会继续到 ReadFromPipe();

使用namepipe的Overlap模式或者尝试创建2个线程和2个namepipes,一个从namepipe读取然后打印出来,另一个从stdin读取然后写入namepipe(在两个进程中)。

【讨论】:

以上是关于使用 ReadFile 和 WriteFile 时出现死锁的主要内容,如果未能解决你的问题,请参考以下文章

28-Jenkins-Pipeline-Pipeline Basic Steps插件之readFile和writeFile方法

WriteFile与ReadFile实例(实用)

Windows API一日一练(54)WriteFile和ReadFile函数

同步I/O 和 异步I/O

node 之 fs流读写

fprintf vs WriteFile 写入管道:无法从所有管道读取