首页 > 系统相关 >深入理解和实现Windows进程间通信(共享内存)

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

时间:2024-06-22 09:01:02浏览次数:34  
标签:文件 HANDLE 映射 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

标签:文件,HANDLE,映射,Windows,间通信,内存,进程,共享内存
From: https://blog.csdn.net/LeoLei8060/article/details/139855380

相关文章

  • 用Rufus工具制作Windows To Go,三步搞定!
    前言前几天小白发了一篇关于使用Dism++工具制作WindowsToGo的教程:简单几步把完整的Windows塞进U盘,小白都能看懂这个在Dism++软件上制作WindowsToGo的教程比在WinPE系统制作简单了一些,但里面有部分操作可能有些小伙伴看不懂。这个没关系!今天小白给小伙伴们带来更加简......
  • Windows C++ 应用软件开发从入门到精通详解
    目录1、引言2、IDE开发环境介绍2.1、VisualStudio 2.2、QTCreator3、Windows平台实用小工具介绍3.1、代码编辑器VSCode3.2、代码查看编辑器SourceInsight3.3、文本编辑器Notepad++3.4、文件搜索工具Everything4、C++语言特性4.1、熟悉泛型编程4.2、了解......
  • Windows 系统安装 NVM(Node Version Manager)攻略
    Windows系统安装NVM(NodeVersionManager)全攻略在Windows系统中,NVM(NodeVersionManager)是一个非常实用的工具,它允许我们方便地管理和切换不同版本的Node.js。在这篇博客中,我将详细介绍Windows系统下安装NVM的步骤、使用方法以及可能出现的问题和解决办法。一、......
  • Windows 系统服务器安装 JDK 配置全攻略
    Windows系统服务器安装JDK配置全攻略在进行Java开发或者部署Java应用程序时,正确安装和配置JDK(JavaDevelopmentKit)是至关重要的一步。在这篇博客中,我将详细介绍在Windows系统服务器上安装JDK的步骤和配置过程。一、准备工作在开始安装JDK之前,我们需要完成......
  • Windows Server 2025 中文版、英文版下载 (Inside Preview, updated Jun 2024) - 下一
    WindowsServer2025中文版、英文版下载(InsidePreview,updatedJun2024)-下一代Windows11ServerWindowsServer2025正式版发布在即请访问原文链接:https://sysin.org/blog/windows-server-2025/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.org我们知道W......
  • Windows Server 2022 OVF, updated Jun 2024 (sysin) - VMware 虚拟机模板
    WindowsServer2022OVF,updatedJun2024(sysin)-VMware虚拟机模板2024年6月版本更新,现在自动运行sysprep,支持ESXiHostClient部署请访问原文链接:https://sysin.org/blog/windows-server-2022-ovf/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.org现在......
  • Windows Server 2019 OVF, updated Jun 2024 (sysin) - VMware 虚拟机模板
    WindowsServer2019OVF,updatedJun2024(sysin)-VMware虚拟机模板2024年6月版本更新,现在自动运行sysprep,支持ESXiHostClient部署请访问原文链接:https://sysin.org/blog/windows-server-2019-ovf/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.orgWindo......
  • Windows Server 2022 中文版、英文版下载 (updated Jun 2024)
    WindowsServer2022中文版、英文版下载(updatedJun2024)WindowsServer2022x64,Version21H2请访问原文链接:https://sysin.org/blog/windows-server-2022/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.org直接上链接,详细说明请访问原文查看。下载地址Window......
  • Windows系统常用介绍
    Windows系统1.重要文件介绍#ProgramFiles软件程序存放目录,64位程序文件默认会安装到这个目录下,安装的时候是可以自行修改安装目录的,如果你安装的某个软件的时候,没有特意的修改安装路径,那么就去这个目录中找​#ProgramFiles(x86) 32位程序文件默认会安装到这个目录......
  • Windows Server 2008 R2 OVF, updated Jun 2024 (sysin) - VMware 虚拟机模板
    WindowsServer2008R2OVF,updatedJun2024(sysin)-VMware虚拟机模板WindowsServer2008R2简体中文版OVF,2024年6月更新请访问原文链接:https://sysin.org/blog/windows-server-2008-r2-ovf/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.orgWindowsSe......