[C++] 匿名管道的理解与实现
什么是匿名管道?
匿名管道用于进程之间通信,且仅限于本地父子进程之间通信,结构简单,类似于一根水管,一端进水另一端出水(单工)。相对于命名管道,其占用小实现简单,在特定情况下,比如实现两围棋引擎本地对战可以使用匿名管道。
怎样实现匿名管道双向通信?
由于匿名管道是单工的,所以为实现父子进程双向通信需要创建两根管道,并由子进程继承一根管道的读句柄和另一根管道的写句柄。
如何理解匿名管道的双向通信?
管道相当于一段内存,一个进程输入,一个进程读出。
在进程通信时一般会产生进程同步问题(进程同步讲解请见操作系统类书籍):父子进程各自均具有读写功能,在管道为空时,相应读进程应该被阻塞起来,直到管道被写入为止才被唤醒。
这种空管道不允许读的特性应当加一个锁,但匿名管道自带了这种功能,所以不需要对读写进行限制,其能自动阻塞。
在VS2017下实现匿名管道
对几个基本点进行介绍
#include <windows.h>
匿名管道需要包含此头文件
首先我们需要了解一下最后程序实现中我想要的效果:父进程输入任意长数字(当然局限于匿名管道的最大大小4MB)通过匿名管道传给子进程,由子进程对该字符串(由于在管道中以字符流形式存在)的各位数进行加和,把这个加和的结果返回父进程。
在实际制作时,我将子进程这个计算函数做成动态链接库的形式进行链入。所以在实际代码中将以一行代码的形式呈现:
int Bitadd(char *ary1, char *ary2, unsigned long len, int Lcount);
其中ary1为子进程接收到的字符串、ary2为计算结果、len是接收到的字符串长度、Lcount为计算结果长度。
创建管道
函数原型:
BOOL WINAPI CreatePipe(
_Out_PHANDLE hReadPipe,
_Out_PHANDLE hWritePipe,
_In_opt_LPSECURITY_ATTRIBUTES lpPipeAttributes,
_In_DWORD nSize);
实际调用形式:
CreatePipe(&read, &write, &sa, 0);
其中read是读句柄,write是写句柄,sa是管道安全属性,0代表管道缓冲设置为系统默认值。
由上函数可知在创建管道之前,需要先设置管道安全属性。
设置管道安全属性
对象原型:
typedef struct _SECURITY_ATTRIBUTES {
DWORD nLength; //结构体的大小,可用SIZEOF取得
LPVOID lpSecurityDescriptor; //安全描述符
BOOL bInheritHandle ;//安全描述的对象能否被新创建的进程继承
} SECURITY_ATTRIBUTES,* PSECURITY_ATTRIBUTES;
在程序中仅需如下设置即可:(ParentView为我创建的父进程管道类)
void ParentView::CreateATTRIBUTES() // 设置管道安全属性
{
sa.bInheritHandle = TRUE; // TRUE为管道可以被子进程所继承
sa.lpSecurityDescriptor = NULL; // 默认为NULL
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
}
各参数在原型中已有很好的注释。
创建好管道后,可以考虑创建子进程,使其继承父进程的管道句柄。
创建子进程
先贴代码:
TCHAR szCmdline[] = TEXT("../../child/Debug/child.exe"); // 设置子进程路径
PROCESS_INFORMATION pi; // 用来接收新进程的识别信息
STARTUPINFO si; // 用于决定新进程的主窗体如何显示
BOOL bSuccess = FALSE;
// 设置PROCESS_INFORMATION
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); // 用0填充内存区域
// 设置STARTUPINFO
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO); // 结构大小
//*************** 句柄继承设置******************
// 创建了两个管道
// 管道1由父进程读,子进程写
// 管道2由父进程写,子进程读
si.hStdError = write1; // 错误输出句柄(在写句柄中写回父进程)
si.hStdOutput = write1; // 子进程继承管道1写句柄
si.hStdInput = read2; // 子进程继承管道2读句柄
//*************** 句柄继承设置******************
si.dwFlags |= STARTF_USESTDHANDLES; // 使用hStdInput 、hStdOutput 和hStdError 成员
// 创建子进程
// 摘自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
// 以上两个字段都可以创建目标子进程
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // bInheritHandles:指示新进程是否从调用进程处继承了句柄
0, // creation flags:指定附加的、用来控制优先类和进程的创建的标志。
// 设置为 CREATE_NEW_CONSOLE 可显示子窗口
NULL, // use parent's environment
NULL, // use parent's current directory
&si, // STARTUPINFO :指向一个用于决定新进程的主窗体如何显示的STARTUPINFO结构体
&pi // PROCESS_INFORMATION :指向一个用来接收新进程的识别信息的PROCESS_INFORMATION结构体
);
// If an error occurs, exit the application.
if (!bSuccess)
cout << "创建子程序失败" << endl;
else
{
// 关闭一些子进程用的句柄
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
CloseHandle(write1);
CloseHandle(read2);
}
首先设置子进程所在路径,子进程为一个exe可执行程序。然后会用到两个类型STARTUPINFO和PROCESS_INFORMATION,有兴趣的朋友可自行百度,查看两种类中的参数。
这里也不贴CreateProcess的函数原型了,代码块中有较好的注释。
其实对于管道创建和子进程创建都是一个模版框架。
读写函数请见github源代码
实现双向通信
在父进程中创建两个匿名管道。此时父进程共有六个句柄Read1,Write1,Read2,Write2,标准输入输出句柄。
由图所示,标准输入输出句柄用于在Dos窗口的输入和输出。
然后我们需要让创建的子进程继承Write1句柄和Read2句柄。
子进程初始化句柄代码
read = GetStdHandle(STD_INPUT_HANDLE); // 继承句柄
write = GetStdHandle(STD_OUTPUT_HANDLE);
if ((read == INVALID_HANDLE_VALUE) || (write == INVALID_HANDLE_VALUE))
cout << "继承句柄无效" << endl;
可以看到,子进程的标准输入输出句柄已经被继承的Write1句柄和Read2句柄所覆盖。
因此无法实现在子进程的Dos窗口进行显示,子进程窗口将是永远黑窗,可以在父程序中注释掉子进程所继承的写句柄进行对比,并将CreateProcess函数中的一个参数设置为显示子进程窗口(注释中有)。
需要注意的坑点:
si.dwFlags |= STARTF_USESTDHANDLES;
若要实现双向通信,子进程Dos是黑窗。但是可以将子进程收到的结果写到文件。
源代码地址:https://github.com/YuxiangTang/AnonymousPipe
原文:https://blog.csdn.net/it2153534/article/details/79064643
[C++] 匿名管道的理解与实现相关推荐
- 进程间通信——匿名管道
使用匿名管道做进程通信,需要用父进程创建一个子进程,该子进程的标准输入输出句柄由父进程指定. 无论父进程还是子进程,都可以收发数据,这里仅演示父进程发数据,子进程打印数据. 父进程循环从控制台读数据, ...
- 进程间通信(匿名管道)
1.进程通信的目的 (1) 数据传输: 一个进程需要将它的数据传输给另一个进程 (2) 资源共享: 多个进程之间共享同样的资源 (3) 通知事件: 一个进程需要向另一个或一组进 ...
- Linux系统编程27:进程间通信之管道的基本概念和匿名管道与命名管道及管道特性
文章目录 (1)管道是什么 (2)匿名管道 A:读端和写端 B:建立匿名管道的函数 C:最简单的进程间通信-演示 D:管道四大特性 E:管道的特点 F:从内核角度理解管道 G:管道总结 (3)命名管道 ...
- 【编撰】linux IPC 002 - 匿名管道PIPE和有名管道FIFO的概念和实例,以及应用比较
前言:上一节提到IPC的概述,本一节,原文作者比较详细的讨论了,管道的概念和实例和使用场景: 原文作者:郑彦兴 (mlinux@163.com)国防科大计算机学院 http://www.ibm.com ...
- Linux IPC:匿名管道 与 命名管道
目录 一.管道的理解 二.匿名管道 三.命名管道 四.管道的通信流程 五.管道的特性 进程间通信方式有多种,本文介绍的是管道,管道分为匿名管道和命名管道. 一.管道的理解 生活中的管道用来传输 ...
- Linux——匿名管道、命名管道及进程池概念和实现原理
目录 一.什么是匿名管道 二.如何使用匿名管道 (一).pipe原理 (二).pipe使用 三.命名管道概念及区别 (一).什么是命名管道 (二).与匿名管道的联系和区别 四.命名管道的使用 (一). ...
- 【Linux】进程间通信--管道(匿名管道和命名管道)
文章目录 前言 进程间通信的目的 管道 匿名管道 管道特点 站在文件描述符角度理解管道 匿名管道通信读写特点 命名管道 命名管道的原理 命名管道的创建 命名管道完成两个不同进程通信 匿名管道和命名管道 ...
- Linux进程通信——匿名管道、命名管道、管道的特性和共享内存
Linux进程通信--匿名管道.命名管道.管道的特性和共享内存 一.管道 1.1 什么是管道? 1.2 匿名管道 <1> 匿名管道参数说明 <2> fork共享管道原理 < ...
- C++学习:第六章Linux高级编程 - (七)信号、sigqueue、sigaction、IPC、管道、匿名管道
回顾: 1. 信号的作用 2. 理解信号: 软中断 可靠与不可靠信号 kill -l 3. 信号发送与注册 kill/raise alarm setitimer signal 4. 信号的屏蔽 sig ...
最新文章
- 天池大赛 + CV语义分割 + 78万奖金:全国数字生态创新大赛来了!
- BIND 子域授权的实现和区域转发实现
- 阶段1 语言基础+高级_1-3-Java语言高级_02-继承与多态_第1节 继承_2_继承的格式
- java的autotype,关于 fastjson 异常 autoType is not support 问题分析解决
- 后台开发人员面试内容——Redis非关系数据库(三)
- vue 常用功能和命令
- python使用joblib多进程执行for循环
- 【数字信号】基于matlab 8级m序列【含Matlab源码 353期】
- Java setlocale方法_Java Locale.Builder setLocale(Locale)用法及代码示例
- React项目中使用Dplayer播放FLV格式视频教程(不支持ios)
- STM32 Flash读写;Flash地址对应的存储内容及方式;
- 启锐 588 打印机每次打印都流出一部分,没有重新切换纸张
- 使用计算机开机按啥建,学生计算器第一次使用应该如何开机
- C语言写出一个随机生成1-100数字的猜数字游戏
- 韩团god朴俊亨迎娶小13岁空姐 成员唱祝歌
- 2021年建议你要早点进入IT行业
- 扩散模型到GLIDE
- sybase安装步骤
- echarts饼状图数据过多时,数据叠加(问题篇)
- java的百钱百鸡,来自我国古代数学家张丘建在《算经》中一道题
热门文章
- c字符串截取一部分字符串_Python如何截取一段字符串?
- mysql删除数据不会减少存储占用_Mysql单文件存储删除数据文件容量不会减少的bug与解决方法...
- cmd修改服务器命令,修改服务器的ip地址的命令行
- 计算机检索的pdf格式,win10系统实现全文搜索pdf文件的解决办法
- python 设置横坐标刻度_python 双误差棒(上下误差棒)主刻度 副刻度
- mysql explain using_[MySQL] explain中的using where和using index
- 18桥图片_世界上第一台70米泵车,采用10桥底盘,泵送速度可达200m/h
- 用积木做了个无人机。
- 嫦娥五号完美落月,背后黑科技令人惊叹
- 单片机的程序有多大?