深入理解和实现Windows进程间通信(共享内存)

news2024/11/25 19:49:29

常见的进程间通信方法

常见的进程间通信方法有:

  1. 管道(Pipe)
  2. 消息队列
  3. 共享内存
  4. 信号量
  5. 套接字

下面,我们将详细介绍共享内存的原理以及具体实现。

什么是共享内存?

Windows共享内存(Shared Memory in Windows)是一种操作系统机制,允许不同的进程(程序)共享一段内存空间。这意味着多个进程可以同时访问同一个内存区域,用以交换数据或进行通信,这是进程间通信(IPC)的一种形式。使用共享内存通常可以提高应用程序之间的数据交换效率,因为它避免了数据的复制过程,直接在内存中进行读写。

共享内存的实现方式

在Windows系统中,共享内存的实现通常有以下几种方式:

  1. 内存映射文件
  • 这是最常见的实现共享内存的方式。通过将磁盘上的文件映射到内存地址空间,文件的内容可以被映射到多个进程的地址空间,从而实现共享。
  1. 命名管道
  • 虽然主要用于进程间的消息传递,命名管道也可以配置为在内存中传输数据,从而模拟共享内存的效果。
  1. 剪贴板
  • 剪贴板提供了一种将数据存储在共享内存中的方法,这样不同的程序可以访问和修改这些数据。
  1. 全局原子表
  • 全局原子表允许程序创建小段的、全局可访问的数据(原子),这些数据可由其他程序读取和修改。

本文只介绍内存映射文件这种方式。

内存映射文件原理

文件与内存的映射

内存映射文件通过将磁盘上的文件或一段虚拟内存与进程的地址空间进行映射来工作。这种映射实际上是创建了文件内容与进程虚拟地址空间之间的直接联系。

操作系统的角色

操作系统负责管理内存和磁盘文件之间的映射关系。当一个文件被映射到内存时,操作系统将文件的一部分或全部内容呈现为进程虚拟内存的一部分。这样,对这部分虚拟内存的访问就相当于直接读写文件内容。

虚拟内存管理

分页机制

  • 操作系统使用分页机制来管理物理内存和虚拟内存。内存映射文件利用这一机制,将文件的内容按页对应到虚拟内存页上。
  • 当进程访问这些虚拟页时,如果对应的物理页不在内存中(即页面错误),操作系统将从磁盘中加载所需的数据页到物理内存中。

写时复制(Copy-on-Write)

  • 对于共享内存映射,操作系统可能使用写时复制策略。这意味着当进程试图写入共享内存时,系统会为该进程创建这部分内存的私有副本,从而保护原始内存内容。

延迟加载

内存映射文件通常不会在映射时立即加载整个文件内容。而是采用延迟加载的方式,即只有在实际访问某个内存区域时,相应的文件部分才被加载到物理内存中。这样可以提高效率,减少内存消耗。

同步和一致性

操作系统还负责同步映射文件的内存视图和磁盘上的文件内容。当进程修改了映射的内存后,这些变更可能会延迟写回到磁盘文件中。这涉及到内存和磁盘操作的一致性和同步问题。

性能优势

内存映射文件提供了比传统的文件I/O更快的数据访问速度,因为它避免了多次的数据复制和用户空间与内核空间之间的上下文切换。数据直接在内存中修改,只在必要时进行磁盘I/O操作。

接口介绍

CreateFileMappint

功能

基于实际的磁盘文件或系统分页文件来创建内存映射文件对象。

声明

HANDLE CreateFileMapping(
  HANDLE hFile,
  LPSECURITY_ATTRIBUTES lpAttributes,
  DWORD flProtect,
  DWORD dwMaximumSizeHigh,
  DWORD dwMaximumSizeLow,
  LPCTSTR lpName
);

参数

  • hFile:文件句柄,INVALID_HANDLE_VALUE用于系统分页文件。
  • lpAttributes:安全属性,通常为NULL
  • flProtect:保护属性,读写权限
    • PAGE_READONLY:分配的页面只读
    • PAGE_READWRITE:分配的页面可读可写
    • PAGE_WRITECOPY:分配的页面写时复制,就是当多个进程映射到同一个内存区域进行读写操作时,它们实际上是在读取同一份数据的副本。但是,当任何一个进程尝试修改这些数据时,操作系统会为该进程创建这部分数据的私有副本,从而隔离修改操作,确保其他进程看到的数据仍然是未被修改的原始数据
    • PAGE_EXECUTE:分配的页面可执行,不可写入。这通常用于执行代码,而非存储数据
    • PAGE_EXECUTE_READ:分配的页面可执行和可读。这适用于执行某些代码,同时需要从相同的内存区域读取数据
    • PAGE_EXECUTE_READWRITE:分配的页面可执行、可读写。这是最灵活的权限,允许执行代码并修改数据
    • PAGE_EXECUTE_WRITECOPY:分配的页面可执行、可读、且写时复制。对这些页面的写入不会影响到原始数据或其他映射的视图
  • dwMaximumSizeHigh: 映射对象的最大大小(高32位)
  • dwMaximumSizeLow: 映射对象的最大大小(低32位)
  • lpName: 映射对象的名称,可用于进程间共享

OpenFileMapping

功能

用于打开一个已经存在的内存映射文件对象,通常在不同的进程中使用,以访问由CreateFileMapping创建的共享内存区域。

声明

HANDLE OpenFileMapping(
  DWORD dwDesiredAccess,
  BOOL bInheritHandle,
  LPCTSTR lpName
);

参数

  • dwDesiredAccess:访问文件映射的权限,需要和CreateFileMapping设置的权限匹配
    • FILE_MAP_ALL_ACCESS:请求完全访问权限,包括读、写和执行
    • FILE_MAP_READ:请求读权限
    • FILE_MAP_WRITE:请求写权限
    • FILE_MAP_COPY:请求写时复制权限
    • FILE_MAP_EXECUTE:请求执行权限
  • bInheritHandle:句柄是否可以被子进程继承
  • lpName:映射对象的名称

MapViewOfFile

功能

将一个文件映射对象映射到调用进程的地址空间,使得文件内容可以通过指针访问。

声明

LPVOID MapViewOfFile(
  HANDLE hFileMappingObject,
  DWORD dwDesiredAccess,
  DWORD dwFileOffsetHigh,
  DWORD dwFileOffsetLow,
  SIZE_T dwNumberOfBytesToMap
);

参数

  • hFileMappingObject:共享内存对象的句柄
  • dwDesiredAccess:访问类型,参考OpenFileMapping函数的第一个参数
  • dwFileOffsetHigh: 映射视图的文件偏移量(高32位)
  • dwFileOffsetLow: 映射视图的文件偏移量(低32位)
  • dwNumberOfBytesToMap: 映射的字节数,0表示从偏移量到文件末尾

UnmapViewOfFile

功能

断开文件映射对象和调用进程地址空间之间的映射关系。这是在映射后,清理资源前的必要步骤。

声明

BOOL UnmapViewOfFile(
  LPCVOID lpBaseAddress
);

参数

  • lpBaseAddressMapViewOfFile返回的基地址

CloseHandle

功能

关闭句柄。使用完映射对象后,应关闭这些句柄以释放资源。

声明

BOOL CloseHandle(
  HANDLE hObject
);

参数

  • hObject:要关闭的对象句柄

实现

本文将实现两个进程,进程1创建共享内存,并一直更新数据,进程2从共享内存中读取数据并打印输出。

进程1代码

#pragma once
#include <windows.h>
#include <iostream>
#include <string>

int sharedMemoryImpl() {
	// 创建或打开一个命名的内存映射文件对象
	HANDLE hMapFile = CreateFileMapping(
		INVALID_HANDLE_VALUE,    // 使用系统分页文件
		NULL,                    // 默认安全性
		PAGE_READWRITE,          // 可读写权限
		0,                       // 最大对象大小(高位)
		256,                     // 最大对象大小(低位)
		L"Local\\MySharedMemory"); // 名称

	if (hMapFile == NULL) {
		std::cerr << "Could not create file mapping object: " << GetLastError();
		return 1;
	}

	// 映射缓冲区视图
	char* pBuf = (char*)MapViewOfFile(hMapFile, // 映射对象句柄
		FILE_MAP_ALL_ACCESS,  // 可读写许可
		0,
		0,
		256);                  // 映射大小

	if (pBuf == NULL) {
		std::cerr << "Could not map view of file: " << GetLastError();
		CloseHandle(hMapFile);
		return 1;
	}

	// 创建事件对象用于同步
	HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, L"Local\\MySharedEvent");
	if (hEvent == NULL) {
		std::cerr << "Could not create event object: " << GetLastError();
		UnmapViewOfFile(pBuf);
		CloseHandle(hMapFile);
		return 1;
	}

	// 写入初始数据
	CopyMemory(pBuf, "Hello, number 1", 15);
	int number = 1;

	while (true) {
		// 增加数字并更新内存
		sprintf_s(pBuf, 256, "Hello, number %d", ++number);
		std::cout << "Data written to memory: " << pBuf << std::endl;

		// 通知进程2
		SetEvent(hEvent);

		// 等待1秒
		Sleep(1000);
	}

	// 清理
	UnmapViewOfFile(pBuf);
	CloseHandle(hMapFile);
	CloseHandle(hEvent);

	return 0;
}

进程2代码

#pragma once
#include <windows.h>
#include <iostream>

int sharedMemoryImpl() {
	// 打开映射文件对象
	HANDLE hMapFile = OpenFileMapping(
		FILE_MAP_ALL_ACCESS,   // 最大访问权限
		FALSE,                 // 继承性标志
		L"Local\\MySharedMemory");  // 对象名称

	if (hMapFile == NULL) {
		std::cerr << "Could not open file mapping object: " << GetLastError();
		return 1;
	}

	// 映射缓冲区视图
	char* pBuf = (char*)MapViewOfFile(hMapFile, // 映射对象句柄
		FILE_MAP_ALL_ACCESS,  // 访问模式
		0,
		0,
		256);                  // 视图大小

	if (pBuf == NULL) {
		std::cerr << "Could not map view of file: " << GetLastError();
		CloseHandle(hMapFile);
		return 1;
	}

	// 打开事件对象
	HANDLE hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, L"Local\\MySharedEvent");
	if (hEvent == NULL) {
		std::cerr << "Could not open event: " << GetLastError();
		UnmapViewOfFile(pBuf);
		CloseHandle(hMapFile);
		return 1;
	}

	while (true) {
		// 等待事件
		WaitForSingleObject(hEvent, INFINITE);

		// 从共享内存读取数据并打印
		std::cout << "Data read from memory: " << pBuf << std::endl;
	}

	// 清理
	UnmapViewOfFile(pBuf);
	CloseHandle(hMapFile);
	CloseHandle(hEvent);

	return 0;
}

结果

Video_2024-06-21_102211.gif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1845848.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

单商户社区团购卖菜卖水果商城自提点商城系统小程序源码

打造便捷团购新体验 &#x1f34e; 引言&#xff1a;社区团购的崛起 近年来&#xff0c;社区团购以其独特的优势&#xff0c;迅速崛起并受到广大消费者的喜爱。它不仅能够提供物美价廉的商品&#xff0c;还能让居民们享受到更加便捷的购物体验。而单商户社区团购系统小程序&am…

Airtest-Selenium实操小课④:微信读书上阅读书籍

1. 前言 上一课我们讲到用Airtest-Selenium爬取下载可爱的猫猫图片&#xff0c;还没看的同学可以戳这里看看~ 那么今天的推文&#xff0c;我们就来说说看&#xff0c;怎么实现模拟真人去打开微信读书网站&#xff0c;点击进入书本进行阅读。 2.需求分析和准备 整体的需求大…

告别繁琐录入,一键解锁OCR魅力:高效文字识别工具推荐

在日常工作中&#xff0c;我们常常会需要找一些模版&#xff0c;如果直接下载编辑某某文档&#xff0c;都是要花钱的。 比如领导让我找个法律文书&#xff0c;改成我们的内容&#xff0c;网上有很多的参考文档&#xff0c;但是不论哪个&#xff0c;下载都要钱&#xff0c;也不…

STM32 CAN总线通讯

使用STM32的CAN通讯&#xff0c;利用回环模式&#xff0c;按键控制发送CAN数据&#xff0c;中断接收CAN数据并通过串口助手打印出来。 7.2、配置引脚信息 由于每次新建工程都需要配置信息&#xff0c;比较麻烦&#xff0c;好在STM32CubeIDE提供了导入.ioc文件的功能&#xff…

阿里云PAI主机网页访问测试

笔者使用的阿里云平台PAI主机(首次使用免费三个月额度)&#xff0c;由于其默认不设置公网IP&#xff0c;所以在该主机上启动HTTP服务后无法访问测试。 这里使用ssh来作隧道穿透&#xff0c;首先需要配置ssh。 云主机配置ssh 1. 修改root账号密码 在云主机上执行 passwd ro…

图片转pdf,图片转pdf在线转换,在线图片转pdf

图片转PDF&#xff0c;听起来似乎是一个简单的操作&#xff0c;但实际上&#xff0c;它涉及到许多细节和技巧。有时候我们需要将图片转换为PDF格式&#xff0c;以便于分享、打印或保存。那么&#xff0c;如何将图片转换成PDF呢&#xff1f;接下来&#xff0c;我将为您详细介绍几…

电脑系统重装怎么操作?分享四个win10重装系统方法

“我遇到了一些笔记本电脑的问题&#xff0c;别人告诉我解决这个问题需要重新安装Win10电脑系统。但我不记得我把光盘放在哪里了&#xff0c;我能否在不丢失文件的情况下重新安装操作系统&#xff1f;电脑系统重装怎么操作&#xff1f;”虽然电脑自带系统中有多种方法可供选择&…

有哪些常用ORM框架

ORM&#xff08;Object-Relational Mapping&#xff0c;对象关系映射&#xff09;是一种编程技术&#xff0c;它允许开发者使用面向对象的编程语言来操作关系型数据库。ORM的主要目的是将数据库中的数据表映射到编程语言中的对象&#xff0c;从而使得开发者可以使用对象的方式来…

Docker之overlay2的迁移

原因 docker默认将文件及其容器放置在了系统盘的挂载区内&#xff0c;如果长期使用会发现系统挂载区被overlay2挤爆了,因此在一开始我们将其迁移在大容量外挂磁盘上,就可以避免系统盘被挤爆,放心使用. 具体操作 # 停止容器 systemctl stop docker# 修改容器配置&#xff0c…

Spring+SpringMVC介绍+bean实例化+依赖注入实战

Spring介绍 Spring是一个轻量级的Java 开发框架&#xff0c;核心是IOC&#xff08;控制反转&#xff09;和AOP&#xff08;面向切面编程&#xff09; Spring解决了业务层&#xff08;Service包&#xff09;与其他各层&#xff08;表现层&#xff0c;包括Model&#xff0c;Vie…

无版权图片素材搜索网站,解决无版权图片查找问题

在数字内容创作领域&#xff0c;图片素材的选择至关重要。一张高质量、合适的图片不仅能够吸引读者的眼球&#xff0c;还能有效传达信息。然而&#xff0c;找到既免费又无版权限制的图片素材并非易事。小编将为大家介绍几个解决这一问题的无版权图片素材搜索网站&#xff0c;这…

程序猿大战Python——面向对象——对象属性

什么是属性 目标&#xff1a;了解什么是属性&#xff1f; 在现实生活中&#xff0c;属性就表示固有特征&#xff0c;比如&#xff1a;一辆小轿车的属性有轮胎数、颜色、品牌等。 仔细观察后会发现&#xff0c;属性可以简单理解为与生俱来的特征&#xff0c;比如一个人的姓名、年…

Lazada API接口——一键获取商品买家评论数据信息

一、引言 在电商领域&#xff0c;买家评论是商品销售中不可忽视的重要因素。它们不仅影响着潜在消费者的购买决策&#xff0c;还为商家提供了宝贵的客户反馈。为了满足商家和数据分析师对买家评论数据的需求&#xff0c;我们特别开发了一款针对Lazada平台的接口&#xff0c;其…

30分钟完成一个AI视频,跑通0到1的过程,包含文生图,图生视频的制作

关注公众号&#xff0c;赠送AI/Python/Linux资料 步骤一&#xff1a;写故事 需要给出故事情节&#xff0c;让kimi首先提供一个提示词模版 提示词输入后&#xff0c;就让kimi开始写故事了 一个完整的故事就出来了 非常好&#xff0c;描述一个IT人的一生是一个宏大的主题&#…

后台管理台字典localStorage缓存删除

localStorage里存放了如以下dictItems_开头的字典数据&#xff0c;localStorage缓存是没有过期时间的&#xff0c;需要手动删除。同时localStorage里还存有其他不需要删除的数据。 这里的方案是遍历localStorage&#xff0c;利用正则和所有key进行匹配&#xff0c;匹配到dict…

【有手就会】图数据库Demo教程,实现《诡秘之主》中的人物关系探索

前言 星环社区版家族于近期发布了单机、30s一键启动的StellarDB图数据库&#xff0c;本篇文章将为用户介绍如何使用开发版StellarDB实现人物关系探索。 友情链接&#xff1a;白话大数据 | 关于图数据库&#xff0c;没有比这篇更通俗易懂的啦 TDH社区版本次发布StellarDB社区…

如何选择合适的半桥栅极驱动芯片?KP8530X,KP85402,KP85211A满足你对半桥栅极驱动一切需求

半桥栅极驱动系列KP8530X&#xff0c;KP85402&#xff0c;KP85211A在功率电子领域展现出卓越的性能和可靠的品质。具备诸多显著优势。首先&#xff0c;半桥栅极驱动系列KP8530X&#xff0c;KP85402&#xff0c;KP85211A拥有出色的耐压性能&#xff0c;可承受高达数百伏的电压&a…

ArcGIS制作规划图卫星影像地图虚化效果

文章目录 一、效果展示二、加载数据三、效果制作四、注意事项一、效果展示 二、加载数据 订阅专栏后,从csdn私信查收实验数据资料,加载ArcGIS制作规划图卫星影像地图虚化效果.rar中的数据,如下所示: 三、效果制作 1. 创建掩膜图层 新建一个矢量图层,因为主要是作图需要…

GNSS边坡监测站

TH-WY1随着科技的飞速发展&#xff0c;各种先进的监测技术不断涌现&#xff0c;为边坡安全监测提供了有力保障。其中&#xff0c;GNSS边坡监测站以其高精度、实时性强的特点&#xff0c;受到了广泛关注。 GNSS边坡监测站&#xff0c;全称为全球导航卫星系统边坡监测站&#xf…

1.接口测试-postman学习

目录 1.接口相关概念2.接口测试流程3.postman基本使用-创建请求&#xff08;1&#xff09;环境&#xff08;2&#xff09;新建项目集合Collections&#xff08;3&#xff09;新建collection&#xff08;4&#xff09;新建模块&#xff08;5&#xff09;构建请求请求URLheader设…