共享内存的创建和使用

以下是对 ShareMemoryPubManager::CreateShm 函数的详细解读,结合代码逻辑和 Windows 共享内存机制分析:


1. 函数功能概述

该函数用于创建并映射一个共享内存区域,将其封装到自定义结构体 SwathShareMemory 中,并存储到成员变量 m_shmQueue 中。核心步骤包括:

  • 构造共享内存名称​:基于 shm_prefixshm_id 生成唯一标识。
  • 创建文件映射对象​:调用 CreateFileMapping 创建共享内存。
  • 映射内存视图​:通过 MapViewOfFile 获取指向共享内存的指针。
  • 初始化并存储元数据​:将句柄、指针等信息保存到 SwathShareMemory 结构体。

2. 关键代码解析

​(1)共享内存名称构造
std::string shm_name = shm_prefix + std::to_string(shm_id) + ".shm";
_bstr_t t = shm_name.c_str();      // 转换为宽字符
wchar_t *pwchar = (wchar_t *)t;
std::wstring result = pwchar;      // 最终名称(Windows API 需宽字符)
  • 目的​:Windows 文件映射 API 要求名称使用宽字符(LPCWSTR)。
  • 潜在风险​:若 shm_prefix 包含非 ASCII 字符,转换可能乱码。建议显式使用 std::wstring 避免转换1。

​(2)处理共享内存大小
DWORD dwMaximumSizeHigh = 0;
DWORD dwMaximumSizeLow = (DWORD)shm_size;
if (shm_size > 0xFFFFFFFF) {       // 0xFFFFFFFF = 4GB
    dwMaximumSizeLow = 0xFFFFFFFF;
    dwMaximumSizeHigh = shm_size >> 32; // 取高 32 位
}
  • 原因​:CreateFileMapping 要求将 64 位大小拆分为两个 32 位参数(DWORD)。
  • 限制​:最大支持 16EB(理论上),但实际受系统资源限制4。

​(3)创建文件映射对象
HANDLE shm_fd = CreateFileMapping(
    INVALID_HANDLE_VALUE,   // 使用系统分页文件(非物理文件)
    NULL,                   // 默认安全属性
    PAGE_READWRITE,         // 读写权限
    dwMaximumSizeHigh,      // 内存大小(高 32 位)
    dwMaximumSizeLow,       // 内存大小(低 32 位)
    result.c_str()          // 共享内存名称
);
  • 关键参数​:
    • INVALID_HANDLE_VALUE:表示共享内存由系统分页文件支持,而非磁盘文件。
    • PAGE_READWRITE:允许读写访问。
  • 错误处理​:返回 NULL 表示失败(需用 GetLastError() 获取详细错误码)4。

​(4)映射内存视图
char *shm_data = (char*)MapViewOfFile(
    shm_fd,                 // 文件映射对象句柄
    FILE_MAP_READ | FILE_MAP_WRITE, // 读写权限
    0, 0,                   // 映射偏移量(从文件头开始)
    0                       // 映射全部大小
);
  • 作用​:将共享内存映射到当前进程的地址空间。
  • 失败处理​:关闭句柄 CloseHandle(shm_fd) 并返回错误。

​(5)初始化元数据
SwathShareMemory shm;
shm.m_handle = shm_fd;      // 文件映射句柄
shm.m_ptr = shm_data;       // 共享内存指针
shm.m_name = result;        // 共享内存名称
shm.m_status = ShareMemoryStatus::kProcessDone; // 状态标记

resetFreeFlag(&shm);        // 初始化状态标志(自定义函数)
resetFrameFlag(&shm);       // 同上

m_shmQueue[shm_id] = std::move(shm); // 存储到队列
  • 关键操作​:
    • resetFreeFlagresetFrameFlag 可能是初始化共享内存头部状态(未提供实现)。
    • 使用 std::move 避免复制开销。

3. 潜在问题与改进建议

问题点
  1. 资源泄漏​:
    • MapViewOfFile 失败,已正确关闭句柄 shm_fd
    • 缺失​:成功时未在析构中释放 shm_fdshm_data(需在类析构函数中添加 UnmapViewOfFileCloseHandle)。
  2. 线程安全​:
    • m_shmQueue 的写操作未加锁,多线程并发调用可能导致竞争。
  3. 名称冲突​:
    • 不同进程使用相同 shm_name 可访问同一共享内存,但若名称全局不唯一,可能意外覆盖。
  4. 大小限制​:
    • 32 位进程的单个映射视图最大 2GB(需分块映射)4。
改进建议
// 析构函数中释放资源
ShareMemoryPubManager::~ShareMemoryPubManager() {
    for (auto& [id, shm] : m_shmQueue) {
        UnmapViewOfFile(shm.m_ptr);
        CloseHandle(shm.m_handle);
    }
}

// 线程安全:访问 m_shmQueue 时加锁
std::mutex mtx;
mtx.lock();
m_shmQueue[shm_id] = std::move(shm);
mtx.unlock();

4. 共享内存生命周期

阶段 操作 API/操作
创建 生成文件映射对象 CreateFileMapping
映射 关联进程地址空间 MapViewOfFile
使用 读写数据 通过 shm_data 指针访问
释放 解除映射并关闭句柄 UnmapViewOfFile + CloseHandle

总结

  • 核心流程​:构造名称 → 创建映射 → 映射视图 → 存储元数据。
  • 关键机制​:依赖 Windows 文件映射 API 实现跨进程共享内存。
  • 注意事项​:务必在析构中释放资源、确保名称全局唯一、处理大内存分块映射4,1。
bool ShareMemoryPubManager::CreateShm(size_t shm_id, size_t shm_size, const std::string &shm_prefix)
{
    std::string shm_name = shm_prefix + std::to_string(shm_id) + ".shm";

    _bstr_t t = shm_name.c_str();
    wchar_t *pwchar = (wchar_t *)t;
    std::wstring result = pwchar;

    DWORD dwMaximumSizeHigh = 0;
    DWORD dwMaximumSizeLow = (DWORD)shm_size;
    if (shm_size > 0xFFFFFFFF)
    {
        dwMaximumSizeLow = 0xFFFFFFFF;
        dwMaximumSizeHigh = shm_size >> 32;
    }

    _length = shm_size;

    HANDLE shm_fd = CreateFileMapping(INVALID_HANDLE_VALUE, // 使用无效句柄
                                      NULL,                 // 安全性默认
                                      PAGE_READWRITE,       // 读写权限
                                      dwMaximumSizeHigh,    // 高位DWORD
                                      dwMaximumSizeLow,     // 地位DWORD
                                      result.c_str()        // 共享内存名称
    );

    if (shm_fd == NULL)
    {
        cout << "创建共享内存失败!" << endl;
        return false;
    }

    char *shm_data = (char *)MapViewOfFile(shm_fd, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);

    if (shm_data == NULL)
    {
        CloseHandle(shm_fd);

        cout << "映射共享内存失败!" << endl;
        return false;
    }

    SwathShareMemory shm;

    shm.m_handle = shm_fd;
    shm.m_swathID = -1;
    shm.m_ptr = shm_data;
    shm.m_name = result;
    shm.m_status = ShareMemoryStatus::kProcessDone;

    resetFreeFlag(&shm);
    resetFrameFlag(&shm);

    m_shmQueue[shm_id] = std::move(shm);

    return true;
}

你可能感兴趣的:(C++,c++)