C++ windows进程间通信
最近一直在找共享内存同步的操作,恰好这篇文章有讲解。本文转载:https://blog.csdn.net/bing_bing_bing_/article/details/82875302
方便记录,copy了一份。
2.进程间的通信
2.1进程
本章讲解windows平台下,进程间的通信方式。
进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动,是操作系统动态执行的基本单元。简单的说,进程就是一段程序的执行过程。
进程和线程:进程是系统动态执行的基本单位,也是系统分配资源的基本单位;线程是进程中执行的最小单位,它可以访问进程的共享资源。
进程之间对共享内存等进行读写操作,需要使用互斥机制,常使用Mutex;进程的同步机制包括Event、Semaphore,常使用Semaphore。
进程间的通信不仅包括进程的同步互斥,还包括进程间数据的传输。
进程间常用的通信方式:共享内存、管道、信号量、端口。其中Mutex和Event放在共享内存中讲解,端口会在网络编程那章讲解。
2.2 Shared Memory
Shared Memory称为共享内存,它是进程间数据传输最快的通信方式。由于共享内存是所有进程都可以访问,因此共享内存的操作需要加锁。
现在实现进程A和进程B,进程A对共享内存写数据,进程B对共享内存读数据。
//进程A
#include <iostream>
#include <Windows.h>
using namespace std;
#define BUF_SIZE 4096
HANDLE H_Mutex = NULL;
HANDLE H_Event = NULL;
int main(int argc,char ** argv)
{
//步骤1:创建共享文件句柄
HANDLE shared_file = CreateFileMapping(
INVALID_HANDLE_VALUE,//物理文件句柄
NULL, //默认安全级别
PAGE_READWRITE, //PAGE_READWRITE表示可读可写,PAGE_READONLY表示只读,PAGE_WRITECOPY表示只写
0, //高位文件大小
BUF_SIZE, //低位文件大小
L"ShareMemory" //共享内存名称
);
if (shared_file == NULL)
{
cout<<"Could not create file mapping object..."<<endl;
return 1;
}
//步骤2:映射缓存区视图,得到指向共享内存的指针
LPVOID lpBUF = MapViewOfFile(
shared_file, //已创建的文件映射对象句柄
FILE_MAP_ALL_ACCESS,//访问模式:可读写
0, //文件偏移的高32位
0, //文件偏移的低32位
BUF_SIZE //映射视图的大小
);
if (lpBUF == NULL)
{
cout << "Could not create file mapping object..." << endl;
CloseHandle(shared_file);
return 1;
}
H_Mutex = CreateMutex(NULL, FALSE, L"sm_mutex");//创建一个互斥器
H_Event = CreateEvent(NULL,//表示安全控制,一般为NULL
FALSE,//确定事件是手动置位还是自动置位,传入TRUE表示手动置位,传入FALSE表示自动置位。如果为自动置位,则对该事件调用WaitForSingleObject()后会自动调用ResetEvent()使事件变成未触发状态。
FALSE,//表示事件的初始状态,传入TRUE表示已触发
L"sm_event"//表示事件的名称,传入NULL表示匿名事件
);
//步骤3:操作共享内存
char Buffer[97];
while (1)
{
cout << "A proccess:Please input the content to the process B" << endl;
cin.getline(Buffer,97);
cout << "Buffer: " <<Buffer<< endl;
WaitForSingleObject(H_Mutex, INFINITE); //使用互斥体加锁 获得互斥器的拥有权
memcpy(lpBUF, Buffer, strlen(Buffer)+1);
ReleaseMutex(H_Mutex); //放锁
SetEvent(H_Event);/激活等待的进程
}
CloseHandle(H_Mutex);
CloseHandle(H_Event);
//步骤4:解除映射和关闭句柄
UnmapViewOfFile(lpBUF);
CloseHandle(shared_file);
return 0;
}
//进程B
#include <iostream>
#include <Windows.h>
using namespace std;
HANDLE H_Mutex = NULL;
HANDLE H_Event = NULL;
int main(int argc, char ** argv)
{
//步骤1:打开共享文件句柄
HANDLE shared_file = OpenFileMapping(
FILE_MAP_ALL_ACCESS,//访问模式:可读写
FALSE,
L"ShareMemory" //共享内存名称
);
if (shared_file == NULL)
{
cout << "Could not open file mapping object..." << endl;
return 1;
}
//步骤2:映射缓存区视图,得到指向共享内存的指针
LPVOID lpBUF = MapViewOfFile(
shared_file, //已创建的文件映射对象句柄
FILE_MAP_ALL_ACCESS,//访问模式:可读写
0, //文件偏移的高32位
0, //文件偏移的低32位
0 //映射视图的大小,0表示从偏移量到文件映射的末尾,因为共享文件open端不知道其大小,所以写0
);
if (lpBUF == NULL)
{
cout << "Could not create file mapping object..." << endl;
CloseHandle(shared_file);
return 1;
}
H_Mutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, L"sm_mutex");
if (H_Mutex == NULL)
{
cout << "open mutex failed..." <<endl;
return 1;
}
H_Event = OpenEvent(EVENT_ALL_ACCESS, FALSE, L"sm_event");
if (H_Event == NULL)
{
cout << "open mutex failed..." << endl;
return 1;
}
char Buffer[97];
//步骤3:操作共享内存
while (1)
{
cout << "B proccess:Receive data from process A" << endl;
WaitForSingleObject(H_Event, INFINITE);
WaitForSingleObject(H_Mutex, INFINITE); //使用互斥体加锁
memcpy(Buffer, lpBUF, strlen((char *)lpBUF) + 1);
ReleaseMutex(H_Mutex); //放锁
cout << Buffer << endl;
//system("PAUSE ");
}
CloseHandle(H_Event);
CloseHandle(H_Mutex);
//步骤4:解除映射和关闭句柄
UnmapViewOfFile(lpBUF);
CloseHandle(shared_file);
return 0;
}
以管理员的身份运行命令提示符,先执行进程A的exe文件,再执行进程B的exe文件。
进程A运行结果:
A proccess:Please input the content to the process B
Hello China
Buffer: Hello China
A proccess:Please input the content to the process B
你好,中国!
Buffer: 你好,中国!
A proccess:Please input the content to the process B
进程B运行结果:
B proccess:Receive data from process A
Hello China
B proccess:Receive data from process A
你好,中国!
B proccess:Receive data from process A
2.3 PIPE
pipe称为管道,其分为命名管道、匿名管道。
2.3.1命名管道
//Server.cpp
#include <Windows.h>
#include <iostream>
using namespace std;
#define BUF_SIZE 4096
HANDLE h_Mypipe = NULL;
//步骤1:定义管道名,点表示当前主机,pipe表示管道
#define MY_NAMED_PIPE L"\\\\.\\pipe\\Named_Pipe"
int main(int argc, char ** argv)
{
//步骤2:创建命名管道
h_Mypipe = CreateNamedPipe(
MY_NAMED_PIPE, //为命名管道创建名称
PIPE_ACCESS_DUPLEX, //管道访问方式:PIPE_ACCESS_DUPLEX指双向模式
PIPE_TYPE_MESSAGE | //命名管道句柄的写入方式:以数据块的方式写入管道
PIPE_READMODE_MESSAGE | //命名管道句柄的读取方式:以数据块的方式从管道读取
PIPE_WAIT, //命名管道句柄的等待方式:阻塞方式
PIPE_UNLIMITED_INSTANCES, //管道所能创建的最大实例个数:1~255,
0, //管道的输出缓冲区容量,0表示默认大小
0, //管道的输入缓冲区容量,0表示默认大小 1000, //管道的默认等待超时,单位毫秒
NULL); //管道的安全性,NULL表示windows提供的默认安全
//INVALID_HANDLE_VALUE是CreateNamedPipe返回值,表示创建失败
if (h_Mypipe == INVALID_HANDLE_VALUE)
{
cout << "Create Named_Pipe Failed..." << endl;
return 1;
}
//步骤3:等待客户端的连接
if (!ConnectNamedPipe(h_Mypipe, NULL))
{
cout << "Connect Failed..." << endl;
return 1;
}
else
cout << "Connect Successed..." << endl;
DWORD wLen = 0;
DWORD rLen = 0;
char szBuffer[BUF_SIZE] = { 0 };
//步骤4:读写管道
while (1)
{
//向客户端发送数据
cin.getline(szBuffer, BUF_SIZE);
cout << "服务器端发送数据:" << szBuffer<< endl;
if (!WriteFile(h_Mypipe, szBuffer, strlen(szBuffer) + 1, &wLen, NULL))
cout << "Write Failed..." << endl;
else
cout<<"服务器端发送成功:共"<< wLen<<"byte"<<endl;
//清除缓冲区
//memset(szBuffer, 0, BUF_SIZE);
//读取客户端数据
if (!ReadFile(h_Mypipe, szBuffer, BUF_SIZE, &rLen, NULL))
cout << "Read Failed..." << endl;
else
cout << "服务器接收客户端数据:" << szBuffer << ", 共" << rLen << "byte" << endl;
}
//步骤5:关闭管道
DisconnectNamedPipe(h_Mypipe);
CloseHandle(h_Mypipe);
return 0;
}
//Client.cpp
#include <Windows.h>
#include <iostream>
using namespace std;
#define BUF_SIZE 4096
HANDLE h_Mypipe = NULL;
//步骤1:定义管道名,点表示当前主机,pipe表示管道
#define MY_NAMED_PIPE L"\\\\.\\pipe\\Named_Pipe"
int main(int argc, char ** argv)
{
//步骤2:判断是否有可用的命名管道
//函数WaitNamedPipe:等待某个管道变成可用状态
//形参1:表示命名管道的名称
//形参2:NMPWAIT_USE_DEFAULT_WAIT使用管道创建时的默认超时设定;NMPWAIT_WAIT_FOREVER永远等待
if (!WaitNamedPipe(MY_NAMED_PIPE, NMPWAIT_USE_DEFAULT_WAIT))
{
cout << "No Named_Pipe Accessible..." << endl;
return 1;
}
else
cout << "Named_Pipe Accessible..." << endl;
//步骤3:打开指定命名管道
//函数CreateFile:创建或打开对象(这里对象指的是管道)
h_Mypipe = CreateFile(
MY_NAMED_PIPE, //创建或打开的对象(管道)名称
GENERIC_READ | //对象的访问方式:读访问
GENERIC_WRITE, //对象的访问方式:写访问
0, //对象是否共享:0表示不共享
NULL, //指向一个SECURITY_ATTRIBUTES结构的指针
OPEN_EXISTING, //对象的创建方式:OPEN_EXISTING表示打开对象(管道)
FILE_ATTRIBUTE_NORMAL, //设置对象的属性和标志
NULL);
if (h_Mypipe == INVALID_HANDLE_VALUE)
{
cout << "Open Named_Pipe Failed..." << endl;
return 1;
}
DWORD wLen = 0;
DWORD rLen = 0;
char szBuffer[BUF_SIZE] = { 0 };
//步骤4:读写管道
while (1)
{
//读取服务器端数据
if (!ReadFile(h_Mypipe, szBuffer, BUF_SIZE, &rLen, NULL))
cout << "Read Failed..." << endl;
else
cout << "客户端接收服务器端数据:" << szBuffer << ", 共" << rLen << "byte" << endl;
//清除缓冲区
//memset(szBuffer, 0, BUF_SIZE);
//客户端发送数据
cin.getline(szBuffer, BUF_SIZE);
cout << "客户端发送数据:" << szBuffer << endl;
if (!WriteFile(h_Mypipe, szBuffer, strlen(szBuffer) + 1, &wLen, NULL))
cout << "Write Failed..." << endl;
else
cout << "客户端发送成功:共" << wLen << "byte" << endl;
}
//步骤5:关闭管道
CloseHandle(h_Mypipe);
return 0;
}
服务器端运行结果:
Connect Successed...
hello world
服务器端发送数据:hello world
服务器端发送成功:共12byte
服务器接收客户端数据:你好,中国, 共11byte
客户端运行结果:
Named_Pipe Accessible...
客户端接收服务器端数据:hello world, 共12byte
你好,中国
客户端发送数据:你好,中国
客户端发送成功:共11byte
命名管道可以实现进程之间的全双工通信。服务器端唯一有权创建管道,并等待客户端的连接请求;客户端只能使用已创建的管道,并打开管道与服务器端通信。服务器端和客户端都是可读可写的。
CreateNamedPipe(参数1,参数2,参数3,参数4,参数5,参数6,参数7,参数8)函数作用是创建命名管道。
参数1为命名管道创建名称。
参数2是命名管道的访问方式,PIPE_ACCESS_DUPLEX是双向模式,即服务器进程和客户端进程都可以从管道读写数据,PIPE_ACCESS_INBOUND是服务器只能从管道读数据,客户端只能向管道写数据,PIPE_ACCESS_OUTBOUND是服务器只能向管道写数据,客户端只能从管道读数据。
参数3分为3部分,分别是命名管道句柄的写入方式、命名管道句柄的读取方式、命名管道句柄的等待方式。命名管道句柄的写入方式分为PIPE_TYPE_BYTE和PIPE_TYPE_MESSAGE,PIPE_TYPE_BYTE是以字节流的形式写入管道,PIPE_TYPE_MESSAGE是以数据块(名为消息或报文)的形式写入管道。命名管道句柄的读取方式分为PIPE_READMODE_BYTE和PIPE_READMODE_MESSAGE,PIPE_READMODE_BYTE是以字节流的形式从管道读取数据,PIPE_READMODE_MESSAGE是以数据块(名为消息或报文)的形式从管道读取数据。在字节流模式中,数据以连续的字节在管道中传输,数据之间没有边界,适合大容量数据的传输;在数据块(消息)模式,数据以不连续的消息为基本单位在管道中传输,消息与消息之间有边界,适合小容量数据的传输。命名管道句柄的等待方式分为PIPE_WAIT和PIPE_NOWAIT,PIPE_WAIT是阻塞模式,PIPE_NOWAIT是非阻塞模式,这是对函数WaitNamedPipe()的设置,一般都设置为阻塞模式。
参数4是命名管道能创建的最大实例个数,范围是1~255。它的意思是有多少进程可以等待使用此命名管道,其中PIPE_UNLIMITED_INSTANCES表示255。
参数5是管道的输出缓冲区容量,0表示默认大小;也可以设为实际数据,如4096。
参数6是管道的输入缓冲区容量,0表示默认大小;也可以设为实际数据,如4096。
参数7是管道的默认等待超时,单位毫秒。这是对函数WaitNamedPipe()的等待时间设置。
参数8表示管道的安全性,NULL表示windows提供的默认安全。
返回值:执行成功,返回管道的句柄;执行失败,返回INVALID_HANDLE_VALUE。
WriteFile(参数1, 参数2, 参数3, 参数4, 参数5)函数是将数据写入一个文件或I/O设备。这里的作用是向管道写数据。
参数1是写入数据的文件句柄,这里指的是命名管道的句柄。
参数2是数据存放的缓冲区地址。
参数3是写入的数据长度,单位是字节。
参数4是实际写入管道的字节数。
参数5是指向OVERLAPPED结构体的指针,默认为NULL,表明使用默认的同步I/O方式。
ReadFile()同理,需要注意的是参数3不能使用strlen,因为数据还没读取出来,无法得知数据的大小,必须手动设置需要读取的数据长度或使用sizeof。
WaitNamedPipe(参数1,参数2)函数的作用是等待某个管道变成可用状态。
参数1是管道的名称。
参数2中NMPWAIT_USE_DEFAULT_WAIT使用管道创建时的默认超时设定,NMPWAIT_WAIT_FOREVER表示一直等待。
CreateFile(参数1, 参数2,参数3,参数4,参数5,参数6,参数7)函数的作用是打开或创建文件或I/O设备。
参数1是打开或创建的对象名称,这里指的是命名管道的名称。
参数2是对象的访问方式,GENERIC_READ是读访问,GENERIC_WRITE是写访问。
参数3表示对象是否共享,0表示不共享。
参数4是指向一个SECURITY_ATTRIBUTES结构的指针,不需要了解。
参数5是对象的打开或创建方式,OPEN_EXISTING表示打开已存在的对象。
参数6是设置对象的属性和标志,FILE_ATTRIBUTE_NORMAL表示该对象没有其他属性设置。
参数7是指定具有GENERIC_READ访问方式的模板文件的句柄,不需要了解。
注:以上函数都是windows平台提供的API。
C++ windows进程间通信的更多相关文章
- Windows进程间通信的各种方法
原文:Windows进程间通信的各种方法 进程是装入内存并准备执行的程序,每个进程都有私有的虚拟地址空间,由代码.数据以及它可利用的系统资源(如文件.管道等)组成.多进程/多线程是Windows操作系 ...
- [转]Windows进程间通信的各种方法
http://www.cnblogs.com/songQQ/archive/2009/06/03/1495764.html 道相似,不过它传输数据是通过不可靠的数据报(如TCP/IP协议中的UDP包) ...
- windows进程间通信 .
摘 要: 随着人们对应用程序的要求越来越高,单进程应用在许多场合已不能满足人们的要求.编写多进程/多线程程序成为现代程序设计的一个重要特点,在多进程程序设计中,进程间的通信是不可避免的.Microso ...
- Windows进程间通信(下)
六.动态数据交换(Dynamic Data Exchange) 动态数据交换(DDE)是使用共享内存在应用程序之间进行数据交换的一种进程间通信形式.应用程序可以使用DDE进行一次性数据传输,也可以当出 ...
- Windows进程间通信(中)
二.文件映射 文件映射(Memory-Mapped Files)能使进程把文件内容当作进程地址区间一块内存那样来对待.因此,进程不必使用文件I/O操作,只需简单的指针操作就可读取和修改文件的内容. W ...
- Windows进程间通信(上)
一.管道 管道(pipe)是用于进程间通信的共享内存区域.创建管道的进程称为管道服务器,而连接到这个管道的进程称为管道客户端.一个进程向管道写入信息,而另外一个进程从管道读取信息. 异步管道是基于字符 ...
- Windows进程间通信--命名管道
1 相关概述 命名管道(Named Pipes)是一种简单的进程间通信(IPC)机制.命名管道可以在同一台计算机的不同进程之间,或者跨越一个网络的不同计算机的不同进程之间的可靠的双向或单向的数据通信. ...
- Windows进程间通信--共享内存映射文件(FileMapping)--VS2012下发送和接收
之前以为两个互不相关的程序a.exe b.exe通信就只能通过网络,人家说可以通过发消息,我还深以为不然,对此,我表示万分惭愧. 之前课本上说的进程间通信,有共享内存.管道等之类的,但没有自己操刀写过 ...
- Windows进程间通信—命名管道
命名管道是通过网络来完成进程间的通信,它屏蔽了底层的网络协议细节.我们在不了解网络协议的情况下,也可以利用命名管道来实现进程间的通信.与Socket网络通信相比,命名管道不再需要编写身份验证的代码.将 ...
随机推荐
- webkit内核的浏览器为什么removeAttribute('style')会失效?
做了一些研究,应该算是理清了问题. 首先,我们在这里常说的「属性」(attributes)其实分为两种:内容属性(content attributes)以及 IDL 属性(IDL attributes ...
- 《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #14 虚拟存储子系统的调整
HACK #14 虚拟存储子系统的调整 本节介绍如何使用/proc进行虚拟存储子系统的调整.虚拟空间存储方式在Linux上向应用程序分配内存时,是通过以页面为单位的虚拟存储方式进行的.采用虚拟存储方式 ...
- [ML] CostFunction [Octave code]
function J = computeCostMulti(X, y, theta) m = length(y); % number of training examples J = 0; for i ...
- Selenium2+python自动化63-简易项目搭建
前言 到unittest这里基本上可以搭建一个简易的项目框架了,我们可以用一条run_main.py脚本去控制执行所有的用例,并生成报告,发送邮件一系列的动作 一.新建工程 1.打开pycharm左上 ...
- Mysql总结(一)
数据库命令:创建create database 数据库名 charset=utf8;删除drop database 数据库名;查看所有数据库:show databases;使用数据库:use 数据库名 ...
- linux编程vim设置
linux环境下c网络编程vim编辑工具设置,包括自动缩进,tab键对齐等.
- 基于HALCON的双目立体视觉系统实现
双目立体视觉是机器视觉的一种重要形式,它是基于视差原理并由多幅图像获取物体三维几何信息的方法.双目立体视觉系统一般由双摄像机从不同角度同时获得被测物的两幅数字图像,或由单摄像机在不同时刻从不同角度获得 ...
- 【316】python.requests 读取网页信息
参考:Python:在网页中查找字符串的一般方法--in 参考:python怎么安装requests 参考:Requests 快速上手 操作步骤如下: 添加环境变量,将 python 所在文件夹添加至 ...
- 迷你MVVM框架 avalonjs 学习教程9、类名操作
ms-class是avalon用得最多的几个绑定之一,也正因为如此其功能一直在扩充中.根据时期的不同,分为旧风格与新风格两种. 旧风格是指ms-class-xxx=”expr”,*ms-class-a ...
- SO_KEEPALIVE选项
[SO_KEEPALIVE选项 ] 对于面向连接的TCP socket,在实际应用中通常都要检测对端是否处于连接中,连接端口分两种情况: 1.连接正常关闭,调用close() shutdown()连接 ...