日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

[C++] 匿名管道的理解与实现

發(fā)布時(shí)間:2023/12/18 55 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [C++] 匿名管道的理解与实现 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

什么是匿名管道?
匿名管道用于進(jìn)程之間通信,且僅限于本地父子進(jìn)程之間通信,結(jié)構(gòu)簡(jiǎn)單,類似于一根水管,一端進(jìn)水另一端出水(單工)。相對(duì)于命名管道,其占用小實(shí)現(xiàn)簡(jiǎn)單,在特定情況下,比如實(shí)現(xiàn)兩圍棋引擎本地對(duì)戰(zhàn)可以使用匿名管道。

怎樣實(shí)現(xiàn)匿名管道雙向通信?
由于匿名管道是單工的,所以為實(shí)現(xiàn)父子進(jìn)程雙向通信需要?jiǎng)?chuàng)建兩根管道,并由子進(jìn)程繼承一根管道的讀句柄和另一根管道的寫句柄。

如何理解匿名管道的雙向通信?
管道相當(dāng)于一段內(nèi)存,一個(gè)進(jìn)程輸入,一個(gè)進(jìn)程讀出。

在進(jìn)程通信時(shí)一般會(huì)產(chǎn)生進(jìn)程同步問題(進(jìn)程同步講解請(qǐng)見操作系統(tǒng)類書籍):父子進(jìn)程各自均具有讀寫功能,在管道為空時(shí),相應(yīng)讀進(jìn)程應(yīng)該被阻塞起來,直到管道被寫入為止才被喚醒。

這種空管道不允許讀的特性應(yīng)當(dāng)加一個(gè)鎖,但匿名管道自帶了這種功能,所以不需要對(duì)讀寫進(jìn)行限制,其能自動(dòng)阻塞。

在VS2017下實(shí)現(xiàn)匿名管道
對(duì)幾個(gè)基本點(diǎn)進(jìn)行介紹
#include <windows.h>

匿名管道需要包含此頭文件

首先我們需要了解一下最后程序?qū)崿F(xiàn)中我想要的效果:父進(jìn)程輸入任意長(zhǎng)數(shù)字(當(dāng)然局限于匿名管道的最大大小4MB)通過匿名管道傳給子進(jìn)程,由子進(jìn)程對(duì)該字符串(由于在管道中以字符流形式存在)的各位數(shù)進(jìn)行加和,把這個(gè)加和的結(jié)果返回父進(jìn)程。

在實(shí)際制作時(shí),我將子進(jìn)程這個(gè)計(jì)算函數(shù)做成動(dòng)態(tài)鏈接庫(kù)的形式進(jìn)行鏈入。所以在實(shí)際代碼中將以一行代碼的形式呈現(xiàn):

int Bitadd(char *ary1, char *ary2, unsigned long len, int Lcount);

其中ary1為子進(jìn)程接收到的字符串、ary2為計(jì)算結(jié)果、len是接收到的字符串長(zhǎng)度、Lcount為計(jì)算結(jié)果長(zhǎng)度。

創(chuàng)建管道
函數(shù)原型:

BOOL WINAPI CreatePipe(?
_Out_PHANDLE hReadPipe,?
_Out_PHANDLE hWritePipe,?
_In_opt_LPSECURITY_ATTRIBUTES lpPipeAttributes,
_In_DWORD nSize);

實(shí)際調(diào)用形式:

CreatePipe(&read, &write, &sa, 0);

其中read是讀句柄,write是寫句柄,sa是管道安全屬性,0代表管道緩沖設(shè)置為系統(tǒng)默認(rèn)值。

由上函數(shù)可知在創(chuàng)建管道之前,需要先設(shè)置管道安全屬性。

設(shè)置管道安全屬性
對(duì)象原型:


typedef struct _SECURITY_ATTRIBUTES {
?
DWORD nLength; //結(jié)構(gòu)體的大小,可用SIZEOF取得
?
LPVOID lpSecurityDescriptor; //安全描述符
?
BOOL bInheritHandle ;//安全描述的對(duì)象能否被新創(chuàng)建的進(jìn)程繼承
?
} SECURITY_ATTRIBUTES,* PSECURITY_ATTRIBUTES;
?
在程序中僅需如下設(shè)置即可:(ParentView為我創(chuàng)建的父進(jìn)程管道類)

void ParentView::CreateATTRIBUTES() ?// 設(shè)置管道安全屬性
{
sa.bInheritHandle = TRUE; // TRUE為管道可以被子進(jìn)程所繼承 ?
sa.lpSecurityDescriptor = NULL; // 默認(rèn)為NULL
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
}
各參數(shù)在原型中已有很好的注釋。

創(chuàng)建好管道后,可以考慮創(chuàng)建子進(jìn)程,使其繼承父進(jìn)程的管道句柄。

創(chuàng)建子進(jìn)程
先貼代碼:

? ??
TCHAR szCmdline[] = TEXT("../../child/Debug/child.exe"); // 設(shè)置子進(jìn)程路徑
PROCESS_INFORMATION pi; ?// 用來接收新進(jìn)程的識(shí)別信息
STARTUPINFO si; ?// 用于決定新進(jìn)程的主窗體如何顯示
BOOL bSuccess = FALSE;
?
? ? // 設(shè)置PROCESS_INFORMATION
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); ?// 用0填充內(nèi)存區(qū)域
? ? // 設(shè)置STARTUPINFO
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO); ?// 結(jié)構(gòu)大小
? ? ? ? ? ? ? ? ? ? ? ? ??//*************** 句柄繼承設(shè)置******************
? ? ? ? ? ? ? ? ? ? ? ? ??// 創(chuàng)建了兩個(gè)管道
? ? ? ? ? ? ? ? ? ? ? ? ??// 管道1由父進(jìn)程讀,子進(jìn)程寫
? ? ? ? ? ? ? ? ? ? ? ? ??// 管道2由父進(jìn)程寫,子進(jìn)程讀
si.hStdError = write1; ?????// 錯(cuò)誤輸出句柄(在寫句柄中寫回父進(jìn)程)
si.hStdOutput = write1; ????// 子進(jìn)程繼承管道1寫句柄
si.hStdInput = read2; ? ????// 子進(jìn)程繼承管道2讀句柄
? ? ? ? ? ? ? ? ? ? ? ? ??//*************** 句柄繼承設(shè)置******************
si.dwFlags |= STARTF_USESTDHANDLES; ?// 使用hStdInput 、hStdOutput 和hStdError 成員 ?
??????????// 創(chuàng)建子進(jìn)程
? ? ? ? ?// 摘自msdn:
?// If lpApplicationName is NULL,?
?// the first white space–delimited token of the command line specifies the module name.?
bSuccess = CreateProcess(
? ? ? ? NULL, ?????????// lpApplicationName
? ? ? ? szCmdline, ????// command line?
? ? ? ? ? ? ? ? ? ? ???// 以上兩個(gè)字段都可以創(chuàng)建目標(biāo)子進(jìn)程
? ? ? ? NULL, ?????????// process security attributes?
? ? ? ? NULL, ?????????// primary thread security attributes?
? ? ? ? TRUE, ?????????// bInheritHandles:指示新進(jìn)程是否從調(diào)用進(jìn)程處繼承了句柄
? ? ? ? 0, ?? ? ? ? ?// creation flags:指定附加的、用來控制優(yōu)先類和進(jìn)程的創(chuàng)建的標(biāo)志。
? ? ? ? ? ? ? ? ? ? ???// 設(shè)置為 CREATE_NEW_CONSOLE 可顯示子窗口
? ? ? ? NULL, ?????????// use parent's environment?
? ? ? ? NULL, ?????????// use parent's current directory?
? ? ? ? &si, ? ? ? ? ??// STARTUPINFO :指向一個(gè)用于決定新進(jìn)程的主窗體如何顯示的STARTUPINFO結(jié)構(gòu)體
? ? ? ? &pi ? ? ? ? ???// PROCESS_INFORMATION :指向一個(gè)用來接收新進(jìn)程的識(shí)別信息的PROCESS_INFORMATION結(jié)構(gòu)體
);
?
// If an error occurs, exit the application.?
if (!bSuccess)
? ? cout << "創(chuàng)建子程序失敗" << endl;
else
{
? ? // 關(guān)閉一些子進(jìn)程用的句柄
? ? CloseHandle(pi.hProcess);
? ? CloseHandle(pi.hThread);
? ? CloseHandle(write1);
? ? CloseHandle(read2);
}
首先設(shè)置子進(jìn)程所在路徑,子進(jìn)程為一個(gè)exe可執(zhí)行程序。然后會(huì)用到兩個(gè)類型STARTUPINFO和PROCESS_INFORMATION,有興趣的朋友可自行百度,查看兩種類中的參數(shù)。

這里也不貼CreateProcess的函數(shù)原型了,代碼塊中有較好的注釋。

其實(shí)對(duì)于管道創(chuàng)建和子進(jìn)程創(chuàng)建都是一個(gè)模版框架。


讀寫函數(shù)請(qǐng)見github源代碼


實(shí)現(xiàn)雙向通信
在父進(jìn)程中創(chuàng)建兩個(gè)匿名管道。此時(shí)父進(jìn)程共有六個(gè)句柄Read1,Write1,Read2,Write2,標(biāo)準(zhǔn)輸入輸出句柄。

??????????????????????????????????

由圖所示,標(biāo)準(zhǔn)輸入輸出句柄用于在Dos窗口的輸入和輸出。

然后我們需要讓創(chuàng)建的子進(jìn)程繼承Write1句柄和Read2句柄。

????????????????????????????????????????

子進(jìn)程初始化句柄代碼
read = GetStdHandle(STD_INPUT_HANDLE); // 繼承句柄
write = GetStdHandle(STD_OUTPUT_HANDLE);
if ((read == INVALID_HANDLE_VALUE) || (write == INVALID_HANDLE_VALUE))
? ? cout << "繼承句柄無(wú)效" << endl;
可以看到,子進(jìn)程的標(biāo)準(zhǔn)輸入輸出句柄已經(jīng)被繼承的Write1句柄和Read2句柄所覆蓋。

因此無(wú)法實(shí)現(xiàn)在子進(jìn)程的Dos窗口進(jìn)行顯示,子進(jìn)程窗口將是永遠(yuǎn)黑窗,可以在父程序中注釋掉子進(jìn)程所繼承的寫句柄進(jìn)行對(duì)比,并將CreateProcess函數(shù)中的一個(gè)參數(shù)設(shè)置為顯示子進(jìn)程窗口(注釋中有)。


需要注意的坑點(diǎn):

si.dwFlags |= STARTF_USESTDHANDLES;

若要實(shí)現(xiàn)雙向通信,子進(jìn)程Dos是黑窗。但是可以將子進(jìn)程收到的結(jié)果寫到文件。


源代碼地址:https://github.com/YuxiangTang/AnonymousPipe

原文:https://blog.csdn.net/it2153534/article/details/79064643?
?

總結(jié)

以上是生活随笔為你收集整理的[C++] 匿名管道的理解与实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。