什么是匿名管道?
匿名管道用于进程之间通信,且仅限于本地父子进程之间通信,结构简单,类似于一根水管,一端进水另一端出水(单工)。相对于命名管道,其占用小实现简单,在特定情况下,比如实现两围棋引擎本地对战可以使用匿名管道。

怎样实现匿名管道双向通信?
由于匿名管道是单工的,所以为实现父子进程双向通信需要创建两根管道,并由子进程继承一根管道的读句柄和另一根管道的写句柄。

如何理解匿名管道的双向通信?
管道相当于一段内存,一个进程输入,一个进程读出。

在进程通信时一般会产生进程同步问题(进程同步讲解请见操作系统类书籍):父子进程各自均具有读写功能,在管道为空时,相应读进程应该被阻塞起来,直到管道被写入为止才被唤醒。

这种空管道不允许读的特性应当加一个锁,但匿名管道自带了这种功能,所以不需要对读写进行限制,其能自动阻塞。

在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. 进程间通信——匿名管道

    使用匿名管道做进程通信,需要用父进程创建一个子进程,该子进程的标准输入输出句柄由父进程指定. 无论父进程还是子进程,都可以收发数据,这里仅演示父进程发数据,子进程打印数据. 父进程循环从控制台读数据, ...

  2. 进程间通信(匿名管道)

    1.进程通信的目的     (1) 数据传输: 一个进程需要将它的数据传输给另一个进程     (2) 资源共享: 多个进程之间共享同样的资源     (3) 通知事件: 一个进程需要向另一个或一组进 ...

  3. Linux系统编程27:进程间通信之管道的基本概念和匿名管道与命名管道及管道特性

    文章目录 (1)管道是什么 (2)匿名管道 A:读端和写端 B:建立匿名管道的函数 C:最简单的进程间通信-演示 D:管道四大特性 E:管道的特点 F:从内核角度理解管道 G:管道总结 (3)命名管道 ...

  4. 【编撰】linux IPC 002 - 匿名管道PIPE和有名管道FIFO的概念和实例,以及应用比较

    前言:上一节提到IPC的概述,本一节,原文作者比较详细的讨论了,管道的概念和实例和使用场景: 原文作者:郑彦兴 (mlinux@163.com)国防科大计算机学院 http://www.ibm.com ...

  5. Linux IPC:匿名管道 与 命名管道

    目录 一.管道的理解 二.匿名管道 三.命名管道 四.管道的通信流程 五.管道的特性   进程间通信方式有多种,本文介绍的是管道,管道分为匿名管道和命名管道. 一.管道的理解   生活中的管道用来传输 ...

  6. Linux——匿名管道、命名管道及进程池概念和实现原理

    目录 一.什么是匿名管道 二.如何使用匿名管道 (一).pipe原理 (二).pipe使用 三.命名管道概念及区别 (一).什么是命名管道 (二).与匿名管道的联系和区别 四.命名管道的使用 (一). ...

  7. 【Linux】进程间通信--管道(匿名管道和命名管道)

    文章目录 前言 进程间通信的目的 管道 匿名管道 管道特点 站在文件描述符角度理解管道 匿名管道通信读写特点 命名管道 命名管道的原理 命名管道的创建 命名管道完成两个不同进程通信 匿名管道和命名管道 ...

  8. Linux进程通信——匿名管道、命名管道、管道的特性和共享内存

    Linux进程通信--匿名管道.命名管道.管道的特性和共享内存 一.管道 1.1 什么是管道? 1.2 匿名管道 <1> 匿名管道参数说明 <2> fork共享管道原理 < ...

  9. C++学习:第六章Linux高级编程 - (七)信号、sigqueue、sigaction、IPC、管道、匿名管道

    回顾: 1. 信号的作用 2. 理解信号: 软中断 可靠与不可靠信号 kill -l 3. 信号发送与注册 kill/raise alarm setitimer signal 4. 信号的屏蔽 sig ...

最新文章

  1. 天池大赛 + CV语义分割 + 78万奖金:全国数字生态创新大赛来了!
  2. BIND 子域授权的实现和区域转发实现
  3. 阶段1 语言基础+高级_1-3-Java语言高级_02-继承与多态_第1节 继承_2_继承的格式
  4. java的autotype,关于 fastjson 异常 autoType is not support 问题分析解决
  5. 后台开发人员面试内容——Redis非关系数据库(三)
  6. vue 常用功能和命令
  7. python使用joblib多进程执行for循环
  8. 【数字信号】基于matlab 8级m序列【含Matlab源码 353期】
  9. Java setlocale方法_Java Locale.Builder setLocale(Locale)用法及代码示例
  10. React项目中使用Dplayer播放FLV格式视频教程(不支持ios)
  11. STM32 Flash读写;Flash地址对应的存储内容及方式;
  12. 启锐 588 打印机每次打印都流出一部分,没有重新切换纸张
  13. 使用计算机开机按啥建,学生计算器第一次使用应该如何开机
  14. C语言写出一个随机生成1-100数字的猜数字游戏
  15. 韩团god朴俊亨迎娶小13岁空姐 成员唱祝歌
  16. 2021年建议你要早点进入IT行业
  17. 扩散模型到GLIDE
  18. sybase安装步骤
  19. echarts饼状图数据过多时,数据叠加(问题篇)
  20. java的百钱百鸡,来自我国古代数学家张丘建在《算经》中一道题

热门文章

  1. c字符串截取一部分字符串_Python如何截取一段字符串?
  2. mysql删除数据不会减少存储占用_Mysql单文件存储删除数据文件容量不会减少的bug与解决方法...
  3. cmd修改服务器命令,修改服务器的ip地址的命令行
  4. 计算机检索的pdf格式,win10系统实现全文搜索pdf文件的解决办法
  5. python 设置横坐标刻度_python 双误差棒(上下误差棒)主刻度 副刻度
  6. mysql explain using_[MySQL] explain中的using where和using index
  7. 18桥图片_世界上第一台70米泵车,采用10桥底盘,泵送速度可达200m/h
  8. 用积木做了个无人机。
  9. 嫦娥五号完美落月,背后黑科技令人惊叹
  10. 单片机的程序有多大?