深入理解和实现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

  • 49
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 进程调度是操作系统中重要的概念,它负责决定哪个进程在什么时候执行。操作系统通过使用调度算法来决定优先级和执行顺序,以最大程度地利用系统资源和提高系统性能。 调度算法是指确定进程优先级和执行顺序的具体方法。常见的调度算法包括先来先服务(FCFS)、最短作业优先(SJF)、优先级调度、时片轮转等。不同的调度算法具有不同的特点和适用场景。 在学习和掌握Windows进程控制时,需要了解以下几个方面: 首先,要了解Windows进程的基本概念和特点。Windows操作系统使用多线程的方式来实现进程,每个进程包含一个或多个线程。了解进程的创建、销毁、状态转换等基本操作。 其次,要掌握Windows进程调度的机制。Windows采用抢占式调度,优先级较高的进程可以抢占正在执行的进程资源。了解Windows调度器的工作原理和调度策略,以及如何设置进程优先级。 此外,要了解Windows提供的进程管理工具。Windows操作系统提供了多个实用工具,如任务管理器、进程监控器等,可以查看和管理系统中运行的进程。学习如何使用这些工具来查看进程的状态、资源使用情况等信息。 最后,需要学习Windows进程通信(IPC)的机制。进程通信是不同进程进行数据交换和共享资源的重要方式。了解Windows中的IPC机制,如管道、共享内存、消息队列等。 总之,理解操作系统中进程调度的概念和调度算法,同时学习并掌握Windows进程控制,可以帮助我们更好地理解和应用操作系统的功能,提高系统资源的利用率和系统性能。 ### 回答2: 进程调度是操作系统中的一种重要机制,它负责从就绪队列中选择合适的进程,并分配给CPU执行。调度算法是为了实现合理的进程调度而设计的一套规则和策略。 调度算法有许多种,常见的有先来先服务(FCFS)、最短作业优先(SJF)、轮转法(RR)和优先级调度等。FCFS是最简单的调度算法,按照进程到达时的先后顺序进行调度。SJF算法根据进程的执行时来选择最短的作业先执行。轮转法是将一个时片分配给每个进程,当时片用完后,将进程放到队列末尾,继续执行下一个进程。优先级调度算法根据进程的优先级来选择执行顺序。 在Windows操作系统中,进程控制是通过系统调用来实现的。可以通过创建进程、管理进程和终止进程等系统调用来实现进程的控制。Windows提供了一系列的API函数来进行进程控制,如CreateProcess、TerminateProcess和GetExitCodeProcess等。 学习并掌握Windows进程控制需要了解Windows进程的基本概念,如进程ID(PID)、进程优先级、进程状态等。同时需要了解相关的API函数的使用方式和参数含义。通过调用这些API函数,可以实现进程的创建、终止和切换,以及获取进程的状态和运行信息等。 总之,要理解操作系统中的进程调度概念和调度算法,需要学习相关的知识,并掌握Windows进程控制的基本操作方法,以便更好地进行进程调度和管理。 ### 回答3: 进程调度是操作系统中的重要概念,指的是将多个处于就绪状态的进程按照一定的算法分配给CPU执行的过程。操作系统通过进程调度算法来决定进程的执行顺序,从而实现资源的合理利用和提高系统性能。 调度算法有多种,常见的包括先来先服务(FCFS)调度算法、短作业优先(SJF)调度算法、优先级调度算法和时片轮转调度算法等。 先来先服务(FCFS)调度算法是最简单的一种调度算法,按照进程到达的顺序进行调度,无论进程执行时有多长,都需要等待前面的进程执行完毕才能执行。 短作业优先(SJF)调度算法则根据进程的执行时来进行调度,先调度执行时短的进程,可以减少平均等待时。 优先级调度算法通过为每个进程分配一个优先级,优先级高的进程优先执行。可以根据进程的特点、紧急程度等进行优先级的调整。 时片轮转调度算法将CPU的执行时分成固定大小的时片,每个进程按顺序执行一个时片,若在一个时片内未执行完毕,则将其放入队列尾部继续等待调度。 学习和掌握Windows进程控制主要包括以下几个方面: 1. 进程创建与终止:学习如何使用WinAPI中的相关函数来创建和终止进程。 2. 进程同步与通信:了解进程同步与通信的机制,如互斥量、信号量、事件等,掌握使用这些机制来实现进程的同步与通信。 3. 进程优先级与调度:了解Windows进程优先级的概念和相关API,以及如何使用优先级来调度进程。 4. 多线程编程:学习如何创建和管理多线程,在多线程环境中处理进程相关的问题。 通过学习和掌握上述内容,可以更加深入理解操作系统中进程调度的概念和调度算法,并能够利用Windows提供的进程控制功能进行实际开发和应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值