c++ - 使用 ReadFile 和 WriteFile 时出现死锁
问题描述
我正在开展一个学校项目,我试图在子进程的 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;
}
解决方案
您将无法在父 StdOut 上显示子进程 StdIn 上写入的内容,因为子进程的 stdIn 句柄设置为“r2”。这样您就无法手动在子进程中插入任何内容。我建议使用namepipe,这样孩子的 std 句柄就不会与其他管道句柄混合。
for(;;) 等于 while(1),ReadFile() 直到接收到数据才会返回,所以如果没有异常(排除没有数据传入)发生,循环永远不会中断,代码不会' t 继续 ReadFromPipe();
使用命名管道的重叠模式或尝试创建 2 个线程和 2 个命名管道,一个从命名管道读取然后打印出来,另一个从标准输入读取然后写入命名管道(在两个进程中)。
推荐阅读
- spring-boot - Spring OAuth2 - 将值分配给用户名属性时,它会给出错误“属性中缺少属性'名称'”
- xml - 与awk斗争
- verilog - 模拟寄存器文件时 Modelsim 中的 Verilog 编译器错误
- javascript - 异步函数返回 Promise 挂起
- xamarin - 防止显示大小更改设置影响应用设计 - Xamarin.forms
- javascript - 基于 HTML 中的两个下拉选择动态显示 JSON 数据的 Javascript
- html - 我如何将我的徽标居中并将我的汉堡菜单放在 shopify 首次亮相主题的左侧
- mongodb - 使用管道时 MongoDB 连接/查找不起作用
- node.js - Fastify 插件性能
- spring-boot - 在 Spring Boot Service 中使用 TinyRadius 无法启动 tomcat