使用Windows函数锁定共享内存物理内存遇阻求助
进程间内存共享与物理内存锁定问题
需求背景
为优化进程性能,计划通过独立工具进程常驻约20GB资源在物理内存中,让其他进程直接从内存读取以替代硬盘加载,采用Windows的CreateFileMapping和PrefetchVirtualMemory函数实现。
测试代码
bool CreateMemoryMapping(const char* filename) { long long m_hMappingSize = GetFileSize(filename); DWORD dwMaxSizeHigh = static_cast<DWORD>(m_hMappingSize >> 32); DWORD dwMaxSizeLow = static_cast<DWORD>(m_hMappingSize & 0xFFFFFFFF); m_hMapping = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, dwMaxSizeHigh, dwMaxSizeLow, filename); if (INVALID_HANDLE_VALUE == m_hMapping) { cout << "initial file mapping Handel failed," << endl; CloseHandle(m_hMapping); CloseHandle(m_hFile); return false; } if (!(m_pBase = MapViewOfFile(m_hMapping, FILE_MAP_ALL_ACCESS, NULL, NULL, NULL))) { cout << "Mapping a file into the address space of a process failed," << endl; CloseHandle(m_hMapping); CloseHandle(m_hFile); return false; } unsigned long long start = GetTickCount64(); FileMappingWriteData(filename, m_hMappingSize); unsigned long long end = GetTickCount64(); unsigned long long duration = end - start; float MByte = (m_hMappingSize / 1024.0) / 1024.0; printf("write to sm %f MB:%d ms\n", MByte, duration); return true; } void FileMappingWriteData(const char* filename, UINT64 datasize) { /*DWORD dwBytesRead = 0; BOOL bSuccess = ReadFile(m_hFile, m_pBase, datasize, &dwBytesRead, NULL);*/ void* data = malloc(datasize); FILE* f = nullptr; fopen_s(&f, filename, "rb"); if (f) { read(data, datasize, f); write(m_pBase, datasize, data); } WIN32_MEMORY_RANGE_ENTRY rangeArray; rangeArray.VirtualAddress = m_pBase; rangeArray.NumberOfBytes = datasize; HANDLE hProcess = GetCurrentProcess(); BOOL success = PrefetchVirtualMemory(hProcess, 1, &rangeArray, 0); if (success) cout << "Resident physical memory success"; Sleep(5000); if (VirtualLock(m_pBase, datasize)) cout << "Check to resident physical memory successful"; CloseHandle(hProcess); free(data); }
当前遇到的问题
- 从共享内存读取与直接从SSD读取耗时一致,任务管理器显示页面错误数相同,说明数据未驻留物理内存。
- 对数十MB文件,
PrefetchVirtualMemory返回成功但VirtualLock始终检测失败;对1KB资源,收发端均显示锁定成功,但读取速度仍与直接读硬盘一致,页面错误数也相同。
已尝试的无效方案
- 启用组策略中的「锁定内存页」权限
- 使用Primo cache等内存映射软件
问题分析与解决方案
1. 核心误区:PrefetchVirtualMemory的作用
PrefetchVirtualMemory仅负责将数据预取到系统缓存,不会锁定物理内存。系统内存紧张时仍会将这些页面置换到分页文件,其他进程访问时依旧触发页面错误,这是导致读取速度无提升的根本原因。
2. VirtualLock失败的关键原因
- 权限未正确启用:仅开启组策略的「锁定内存页」不够,进程需主动启用
SeLockMemoryPrivilege权限,否则大内存范围的VirtualLock会直接失败。 - 内存页对齐问题:
VirtualLock要求锁定区域为完整内存页,起始地址需对齐到页边界(Windows默认页大小4KB),若datasize不是页大小的整数倍,也会导致锁定失败。 - 内存配额限制:默认情况下,进程可锁定的内存量有限,20GB的一次性锁定需求远超配额,需分段处理。
3. 正确实现步骤
步骤1:启用进程的内存锁定权限
在创建内存映射前添加权限启用逻辑:
BOOL EnableLockMemoryPrivilege() { HANDLE hToken; TOKEN_PRIVILEGES tp; LUID luid; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) return FALSE; if (!LookupPrivilegeValue(NULL, SE_LOCK_MEMORY_NAME, &luid)) { CloseHandle(hToken); return FALSE; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) { CloseHandle(hToken); return FALSE; } CloseHandle(hToken); return GetLastError() == ERROR_SUCCESS; }
在CreateMemoryMapping开头调用EnableLockMemoryPrivilege()。
步骤2:主动触页+锁定内存
替换原PrefetchVirtualMemory和VirtualLock的调用,通过遍历内存页触发加载,再执行锁定:
void LockMemoryRegion(void* base, UINT64 size) { SYSTEM_INFO sysInfo; GetSystemInfo(&sysInfo); DWORD pageSize = sysInfo.dwPageSize; char* ptr = static_cast<char*>(base); UINT64 remaining = size; // 主动触页,强制将所有页面加载到物理内存 while (remaining > 0) { volatile char dummy = *ptr; // 读取字节触发页加载,volatile防止编译器优化 UINT64 step = min(static_cast<UINT64>(pageSize), remaining); ptr += step; remaining -= step; } // 锁定内存 if (VirtualLock(base, size)) cout << "Memory locked successfully" << endl; else cout << "VirtualLock failed, error code: " << GetLastError() << endl; }
在FileMappingWriteData中调用LockMemoryRegion(m_pBase, datasize)。
步骤3:修正文件写入逻辑
原代码用C标准库write操作内存映射区域存在风险,建议改用ReadFile直接写入映射地址:
// 替换FileMappingWriteData中的文件读取逻辑 HANDLE hFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { DWORD bytesRead; ReadFile(hFile, m_pBase, static_cast<DWORD>(datasize), &bytesRead, NULL); CloseHandle(hFile); }
步骤4:多进程共享注意事项
- 其他进程映射共享内存后无需再次锁定,只要创建映射的进程保持运行并持有锁定,物理内存页面不会被置换。
- 若创建进程退出,共享内存会被销毁,物理内存页面会被释放。
4. 20GB大内存优化建议
- 分段锁定:将20GB内存划分为多个4GB或更小的段,逐个锁定,避免单次锁定内存过大导致失败。
- 使用大页面:启用2MB/1GB大页面可减少页表开销,提升锁定效率。需在组策略中开启「锁定大页面」权限,映射时使用
MapViewOfFileEx并指定FILE_MAP_LARGE_PAGES标志。
内容的提问来源于stack exchange,提问作者xd_m687
相关产品推荐
相关产品推荐

