实践用“共享内存”(Named Shared Memory)实现Windows进程间通信

本文通过示例介绍了Windows下利用共享内存进行进程间通信的方法。详细展示了如何创建和打开共享内存对象,以及如何映射和访问共享内存中的数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目标

我想要知道进程间通信的方法。我在《进程间通信(IPC)介绍 - ZH奶酪 - 博客园》中看到了各种进程间通信的方法,其中它提到共享内存是最快的方式,于是我想实践一下它。

本篇的目标实现这样一个测试:在一个进程中向一块“共享内存”中写数据,在另一个进程中从这块“共享内存”中读数据。

在Windows上,最权威的文档与代码范例是:《Creating Named Shared Memory - Win32 apps | Microsoft Docs》
另外,《Windows共享内存示例 - 可笑痴狂 - 博客园》这篇也对我帮助很大。

使用函数介绍

用到的函数需要#include <windows.h>。下面将讨论“共享内存”相关的函数以及需要注意的参数:

CreateFileMapping

首先,要使用 CreateFileMapping创建一个file mapping object

HANDLE CreateFileMappingA(
  HANDLE                hFile,
  LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
  DWORD                 flProtect,
  DWORD                 dwMaximumSizeHigh,
  DWORD                 dwMaximumSizeLow,
  LPCSTR                lpName
);

其中:

  • hFile参数将设置为INVALID_HANDLE_VALUE

If hFile is INVALID_HANDLE_VALUE, the calling process must also specify a size for the file mapping object in the dwMaximumSizeHigh and dwMaximumSizeLow parameters. In this scenario, CreateFileMapping creates a file mapping object of a specified size that is backed by the system paging file instead of by a file in the file system.

  • flProtect代表保护权限,例如PAGE_READWRITE代表有“读”和“写”的权限。
  • lpName将是“文件”的名字。当然,在我的两个测试进程里这个名字应该一致。
OpenFileMapping

而在读内存的进程中,将使用 OpenFileMapping 打开一个file mapping object

HANDLE OpenFileMappingA(
  DWORD  dwDesiredAccess,
  BOOL   bInheritHandle,
  LPCSTR lpName
);

其中:

  • dwDesiredAccess代表权限,例如FILE_MAP_ALL_ACCESS表示所有的权限包括读写。
  • bInheritHandle

If this parameter is TRUE, a process created by the CreateProcess function can inherit the handle; otherwise, the handle cannot be inherited.

  • lpName将是“文件”的名字。当然,在我的两个测试进程里这个名字应该一致。
MapViewOfFile

使用 MapViewOfFile 可以将CreateFileMappingOpenFileMapping返回的Handle映射到一块缓存中。

LPVOID MapViewOfFile(
  HANDLE hFileMappingObject,
  DWORD  dwDesiredAccess,
  DWORD  dwFileOffsetHigh,
  DWORD  dwFileOffsetLow,
  SIZE_T dwNumberOfBytesToMap
);
  • hFileMappingObject:填写CreateFileMappingOpenFileMapping返回的file mapping object的Handle。
  • dwDesiredAccess:代表权限,例如FILE_MAP_ALL_ACCESS代表有读写权限。
  • dwNumberOfBytesToMap:映射多少字节。(If this parameter is 0 (zero), the mapping extends from the specified offset to the end of the file mapping.

代码实践

写入共享内存的数据的进程:

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

//测试用的数据结构
struct MyTestData
{
    int TestInt;        //测试的整数数据
    char TestStr[5];    //测试的字符串数据
};

int main()
{
    //FMO(file mapping object)的名字(应该在两个测试进程中保持一致)
    const std::wstring FMO_Name(L"TestFMO");   

    //创建一个FMO
    HANDLE hMap = CreateFileMapping(
        INVALID_HANDLE_VALUE,       // use paging file
        NULL,                       // default security
        PAGE_READWRITE,             // 读写权限
        0,                          // maximum object size (high-order DWORD)
        sizeof(MyTestData),         // maximum object size (low-order DWORD)
        FMO_Name.c_str());          // FMO 的名字

    //映射到缓冲中
    void* pBuffer = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
    
    //将指针转换为 MyTestData 类型
    MyTestData* shared_data = (MyTestData*)pBuffer;


    //在循环中不断改变数据
    while (1)
    {
        //写一个随机的数据
        shared_data->TestInt = rand() % 10;
        for (int i = 0; i < 4; i++)
            shared_data->TestStr[i] = 'a' + rand() % 26;
        shared_data->TestStr[4] = '\0';

        //打印信息:
        std::cout << "写入共享内存:" << shared_data->TestInt <<' '<< shared_data->TestStr <<std::endl;

        //停留1秒
        Sleep(1000);
    }


    //解除映射
    UnmapViewOfFile(pBuffer);
    //关闭FMO的Handle
    CloseHandle(hMap);
    
    return 0;
}

读取共享内存的数据的进程:

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

//测试用的数据结构
struct MyTestData
{
    int TestInt;        //测试的整数数据
    char TestStr[5];    //测试的字符串数据
};

int main()
{
    //FMO(file mapping object)的名字(应该在两个测试进程中保持一致)
    const std::wstring FMO_Name(L"TestFMO");

    //打开一个FMO
    HANDLE hMap = OpenFileMapping(
        FILE_MAP_ALL_ACCESS,    // 读/写权限
        FALSE,                  // do not inherit the name
        FMO_Name.c_str());      // FMO 的名字


    //映射到缓冲中
    void* pBuffer = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);

    //将指针转换为 MyTestData 类型
    MyTestData* shared_data = (MyTestData*)pBuffer;


    //在循环中不断读取数据
    while (1)
    {
        //打印信息:
        std::cout << "读取共享内存:" << shared_data->TestInt << ' ' << shared_data->TestStr << std::endl;
        
        //停留0.1秒
        Sleep(100);
    }


    //解除映射
    UnmapViewOfFile(pBuffer);
    //关闭FMO的Handle
    CloseHandle(hMap);

    return 0;
}

效果:
在这里插入图片描述

*另一个测试

我在想,如果共享内存中有指针,其指向了进程A中的一个数据,那么在进程B中,还能通过上面这种方式访问吗?
可惜测试发现是不能的。
在这里插入图片描述
在这里插入图片描述

在另一个进程中读取,发现可能会有多种行为:
在这里插入图片描述

在这里插入图片描述

还有一次没中断,不过数据读到是“烫烫烫。。。”。之后试了几次没能再复现

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值