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

常见的进程间通信方法

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

  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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【linux】内核源码TCP->IP->L2层函数调用继续摸索中

日志打印的时候&#xff0c;把行数也打印了&#xff1a; 登录 - Gitee.comhttps://gitee.com/r77683962/linux-6.9.0/commit/b847489a9910f68b9581fd8788807c697c82cdbd 上回基于应用层wget操作找到TCP调用的一些接口&#xff0c;并且已经到IP层的一些接口&#xff0c;当前基…

Windows系统Maven下载安装

下载&#xff1a; 官网地址&#xff1a;https://maven.apache.org/download.cgi 安装&#xff1a; 下载下来的是一个压缩包&#xff0c;首先将其解压到你的Maven目标安装位置 接下来为其配置其环境变量 &#xff08;Maven的基础是Java&#xff0c;因此要首先确认已为你的电…

求求你别学了:从 Prompt 到 RAG,从 RAG 到 DSPy

如本瓜在此前的文章中提到过&#xff0c;Prompt 工程已经不中用了&#xff0c;没有人愿意废那么大的劲来学习如何结构化提问&#xff0c;大家想要的就是傻瓜式提问&#xff0c;但是大模型的回答还是精准的、合意的&#xff1b; 后来&#xff0c;大兴 RAG 技术&#xff0c;做专…

Nuxt快速学习开发 - Nuxt3静态资源Assets

Nuxt 使用两个目录来处理样式表、字体或图像等资产。 public/目录内容按原样在服务器根目录中提供。 assets/目录包含您希望构建工具&#xff08;Vite 或 webpack&#xff09;处理的所有资产。 public/目录 public目录用作静态资产的公共服务器&#xff0c;可在您的应用程序定…

景联文科技构建高质量多轮对话数据库,赋能AI交互新飞跃

近年来&#xff0c;大语言模型的发展极大推动了自然语言处理领域的进步&#xff0c;大语言模型正引领智能对话领域进入一个全新时代&#xff0c;不仅提升了对话体验的自然度和效率&#xff0c;也为探索更加人性化、智能化的交互方式开辟了道路。 景联文科技作为大语言模型数据服…

文心一言 VS 讯飞星火 VS chatgpt (285)-- 算法导论21.2 4题

四、请给出图 21-3 所示操作序列的一个运行时间的渐近紧确界&#xff0c;假定使用链表表示和加权合并启发式策略。如果要写代码&#xff0c;请用go语言。 文心一言&#xff1a; 图 21-3 没有直接给出&#xff0c;但通常这种操作序列可能与某种数据结构&#xff08;如并查集或…

算法:分治(归并)题目练习

目录 题目一&#xff1a;排序数组 题目二&#xff1a;数组中的逆序对 题目三&#xff1a;计算右侧小于当前元素的个数 题目四&#xff1a;翻转对 题目一&#xff1a;排序数组 给你一个整数数组 nums&#xff0c;请你将该数组升序排列。 示例 1&#xff1a; 输入&#xf…

收银系统源码推荐收银系统二次开发

千呼新零售收银系统是一套线下线上一体化的收银系统&#xff0c;给商户提供了含有线下收银/称重、线上小程序商城、精细化会员管理、ERP进销存、营销活动、移动店务管理等一体化解决方案&#xff01; 1. 多样化线下收银 线下收银支持Windows收银、安卓收银、智能称重收银、无人…

BFS:FloodFill算法

文章目录 FloodFill算法简介1.图像渲染2.岛屿数量3.岛屿的最大面积4.被围绕的区域总结 FloodFill算法简介 Flood Fill算法是一种用于确定与某个给定节点相连的区域的算法&#xff0c;常用于计算机图形学和图像处理。该算法可以用于诸如填充多边形、检测连通区域等任务。Flood …

Linux 一键部署 Nginx1.26.1 + ModSecurity3

前言 ModSecurity 是 Apache 基金会的一个开源、高性能的 Web 应用程序防火墙(WAF),它提供了强大的安全规则引擎,用于检测和阻止各种攻击行为,如 SQL 注入、XSS 跨站点脚本攻击等。而 nginx 是一个高性能的 Web 服务器,常用于处理大量的并发请求,具有很高的负载均衡能力…

蓝牙数传芯片TD5325A,蓝牙5.1—拓达半导体

拓达TD5325A芯片是一款支持蓝牙BLE&SPP的纯数传芯片&#xff0c;蓝牙5.1版本。芯片的亮点在于性能强&#xff0c;支持APP端直接对芯片做设置与查询操作&#xff0c;包括修改蓝牙名、UUID、MAC地址&#xff0c;以及直接操作蓝牙芯片自身的IO与PWM口&#xff0c;还包括支持简…

Linux:用户账号和权限管理的命令

目录 一、Linux用户的分类和组的分类 1.1、用户账号和组账号 1.2、用户的分类 1.3、组账号 1.4、用户账号文件/etc/passwd 二、用户管理相关命令 2.1、chage命令&#xff1a;用来修改帐号和密码的有效期限&#xff0c;针对目前系统已经存在的用户 2.2、useradd&#xf…

行车记录仪文件夹“0字节”现象解析与恢复策略

一、行车记录仪文件夹“0字节”现象描述 行车记录仪作为现代驾驶中的必备设备&#xff0c;其储存的视频数据对于事故记录和取证至关重要。然而&#xff0c;有时车主们可能会遇到这样一个问题&#xff1a;行车记录仪的某个文件夹内的文件突然变成了0字节大小&#xff0c;无法正…

Vue快速上手和Vue指令

一、Vue快速上手 1、Vue概念 Vue (读音 /vjuː/&#xff0c;类似于 view) 是一套构建用户界面的渐进式框架 Vue2官网&#xff1a;https://v2.cn.vuejs.org/ 构建用户界面&#xff1a;基于数据渲染出用户可以看到的界面 渐进式&#xff1a; 循序渐进&#xff0c;不一定非得把…

操作系统精选题(一)(PV经典问题之生产者与消费者)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;操作系统 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 前言 进程互斥与同步 题目一 题目二 题…

开发uniapp插件包aar文件,使uniapp可以调用jar包

背景 使用 uniapp 开发应用时&#xff0c;很多时候都需要调用第三方的sdk&#xff0c;一般以 jar 为主。为了应对这个问题&#xff0c;官方提供了插件方案&#xff0c;可以将第三方 jar 包进行封装为 aar 包后&#xff0c;再集成到 uniapp 中使用。 一、环境安装工具 1、jdk…

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

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

qt打包失败 ,应用程序无法正常启动0xc000007b解决办法

用 windeployqt 打包QT程序&#xff0c;运行时提示程序无法正常启动0xc000007b #原因&#xff1a;因本机装了多个版本的Qt&#xff0c;包括32位&#xff0c;64位的&#xff0c;在cmd下可能是环境变量原因&#xff0c;用 windeployqt 打的包无法运行 解决办法&#xff1a; 1、…

【netty】三万字详解!JAVA高性能通信框架,关于netty,看这一篇就够了

目录 1.概述 2.hello world 3.EventLoop 4.channel 4.1.同步 4.2.异步 4.3.调试 4.4.关闭 4.5.为什么要用异步 5.future 6.promise 7.pipeline 8.byteBuf 8.1.创建 8.2.内存模式和池化 8.2.1.内存模式 8.2.2.池化 8.3.组成 8.4.操作 8.4.1.读写 8.4.2.释放…

内容安全复习 2 - 网络信息内容的获取与表示

文章目录 信息内容的获取网络信息内容的类型网络媒体信息获取方法 信息内容的表示视觉信息视觉特征表达文本特征表达音频特征表达 信息内容的获取 网络信息内容的类型 网络媒体信息 传统意义上的互联网网站公开发布信息&#xff0c;网络用户通常可以基于网络浏览器获得。网络…