#include "pch.h" #include "Mem\CMem.h" #include #include "AlignSize.h" //#include "CSystemSemaphore.h" #include "aLog.h" // 静态成员变量 size_t CMemUsage::totalContainerSize=0; size_t CMemUsage::totalMemSize=0; std::list CMemUsage::MemList; std::mutex CMemUsage::MemListLock; std::mutex CMem::uplocker; void CMemUsage::report(std::wstring& str) { std::lock_guard guard(MemListLock); // 加锁 _updateUsage(); str = L"内存使用统计:\n"; for (auto mem : MemList) { if(mem->memName.empty()) { str += fmt::format(L"未命名, {}, {}k/{}k, {}M/{}M\n", mem->memType.c_str(), mem->memSize / 1_K, mem->containerSize / 1_K, mem->memSize / 1_M, mem->containerSize / 1_M); }else { str += fmt::format(L"{}, {}, {}k/{}k, {}M/{}M\n", mem->memName.c_str(), mem->memType.c_str(), mem->memSize / 1_K, mem->containerSize / 1_K, mem->memSize / 1_M, mem->containerSize / 1_M); } } str += fmt::format(L"\n总内存:{}M/{}M\n", totalMemSize / 1_M, totalContainerSize / 1_M); } bool CMem::_allocVirtual(void* addr, size_t size, bool commit) { const DWORD flag = (commit) ? (MEM_RESERVE | MEM_COMMIT) : MEM_RESERVE; // 执行windows api Addr = static_cast(VirtualAlloc( addr, // 0: 系统自动分配地址, 其他值:指定未使用的内存地址 size, // Size of allocation flag, PAGE_READWRITE)); //只支持Read + Write,未考虑其他情况 // 结果 if (Addr) { // 分配成功 containerSize = size; memSize = (commit) ? size : 0; selfAlloc = true; return true; } else { // 调用处来处理不成功的情况,这里不管 containerSize = 0; memSize = 0; selfAlloc = false; return false; } } uint8_t* CMem::allocVirtual(size_t size) { // 执行windows api return static_cast(VirtualAlloc( nullptr, // 0: 系统自动分配地址, 其他值:指定未使用的内存地址 size, // Size of allocation MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)); //只支持Read + Write,未考虑其他情况 } void CMem::freeVirtual(uint8_t* addr) { if(addr) VirtualFree(addr, 0, MEM_RELEASE); // 未处理失败情况,假设不会失败 // 此处addr未清零,在dealloc()中清零 } bool CMem::_allocUpperVirtual(size_t size, bool commit) { const DWORD flag = (commit) ? (MEM_RESERVE | MEM_TOP_DOWN | MEM_COMMIT) : (MEM_RESERVE | MEM_TOP_DOWN); // 执行windows api Addr = static_cast(VirtualAlloc( nullptr, // System selects address size, // Size of allocation flag, PAGE_READWRITE)); //只支持Read + Write,未考虑其他情况 // 结果 if (Addr) { containerSize = size; memSize = 0; selfAlloc = true; return true; } else { // 调用处来处理不成功的情况,这里不管 containerSize = 0; memSize = 0; selfAlloc = false; return false; } } // 分配虚拟内存 bool CWinMem::alloc(void* addr, size_t container, size_t size) { // addr可以为0或指定某个未分配的地址 ASSERT(container != 0); //虚拟内存大小,不能为0 ASSERT(size <= container);//物理内存大小不超过虚拟内存大小,可以为0 AlignUp(container, 64 * 1024); // 调整虚拟内存大小对齐到64k。win默认按64k分配虚拟内存 AlignUp(size, 4 * 1024); // 调整物理内存大小对齐到4k。win默认按4k分配物理内存 if (size == 0) { return _allocVirtual(addr, container, false); // 不分配物理内存 } else if (size == container) { return _allocVirtual(addr, container, true); // 分配全部物理内存 } else { const bool ret = _allocVirtual(addr, container, false); if (!ret) { selfAlloc = false; return false; } if (nullptr != VirtualAlloc(Addr, size, MEM_COMMIT, PAGE_READWRITE)) { // 分配指定大小的物理内存 memSize = size; selfAlloc = true; } else { selfAlloc = true; // 虽然没有物理内存, 分到虚拟内存了也算selfAlloc return false; } } return true; } bool CWinMem::allocUpper(size_t container, size_t size) { ASSERT(container != 0); ASSERT(size <= container); AlignUp(container, 64_K); AlignUp(size, 4_K); std::lock_guard guard(uplocker); if (size == 0) { return _allocUpperVirtual(container, false); } else if (size == container) { return _allocUpperVirtual(container, true); } else { const bool ret = _allocUpperVirtual(container, false); if (!ret) { selfAlloc = false; return false; } if (nullptr != VirtualAlloc(Addr, size, MEM_COMMIT, PAGE_READWRITE)) { memSize = size; selfAlloc = true; } else { selfAlloc = true; // 虽然没有物理内存, 分到虚拟内存了也算selfAlloc return false; } } return true; } // 调整大小不影响containerSize、Addr和selfAlloc bool CWinMem::resize(size_t size) { if (containerSize == 0) return false; AlignUp(size, 4 * 1024); if (size > containerSize) return false; if (size == memSize) { return true; } else if (size > memSize) { if (nullptr != VirtualAlloc(Addr + memSize, size - memSize, MEM_COMMIT, PAGE_READWRITE)) { memSize = size; return true; } else { return false; } } else { VirtualFree(Addr + size, memSize - size, MEM_DECOMMIT); // 有多的释放掉 memSize = size; return true; } }; // 调整大小不影响containerSize、Addr和selfAlloc bool CWinMem::reserve(size_t size) { if (containerSize == 0) return false; AlignUp(size, 4 * 1024); if (size > containerSize) return false; if (size == memSize) return true; else if (size > memSize) { if (nullptr != VirtualAlloc(Addr + memSize, size - memSize, MEM_COMMIT, PAGE_READWRITE)) { memSize = size; return true; } else { return false; } }else { //VirtualFree(Addr + size, memSize - size, MEM_DECOMMIT); // 有多的不释放 return true; } } void CWinMem::dealloc() { if (Addr) { VirtualFree(Addr, 0, MEM_RELEASE); // 只能release整个区域 } CMem::dealloc(); } std::wstring CWinMem::details() { std::wstring str = fmt::format(L"虚拟内存地址:{}, 大小{}M,物理内存大小{}M\n", (void*)Addr, containerSize / 1_M, memSize / 1_M); return str; } bool CFileMapMem2::allocNamed(const std::wstring& name, size_t container, size_t size) { // 确认调用前的状态,避免逻辑错误 ASSERT(hand == nullptr); // 未创建内存映射文件 ASSERT(containerSize == 0); // 未映射文件 ASSERT(allMem.containerSize == 0); // 未分配虚拟内存 ASSERT(allMem.Addr == nullptr); ASSERT(memSize == 0); // 未映射文件 // 确认参数有效 ASSERT(container > 0); ASSERT(isAlign(container, 64_K)); ASSERT(size ==0); _nativeKey = std::wstring(L"Ks_Grabber_MemMapFile_") + name; std::lock_guard guard(uplocker); allMem.Addr = static_cast(VirtualAlloc( nullptr, // System selects address container * 2, // Size of allocation MEM_RESERVE | MEM_TOP_DOWN, PAGE_READWRITE)); //只支持Read + Write,未考虑其他情况 if(!allMem.Addr) return false; allMem.containerSize = container * 2; allMem.memSize = 0; // Create the file mapping. hand = CreateFileMapping(INVALID_HANDLE_VALUE , nullptr , PAGE_READWRITE | SEC_COMMIT // 不能加SEC_NOCACHE , container >> 32, container & 0xFFFFFFFFu, _nativeKey.c_str()); if (!hand) { VirtualFree(allMem.Addr, 0, MEM_RELEASE); // 释放虚拟内存空间 allMem.containerSize = 0; selfAlloc = false; return false; } // 供外部访问的内存属性 Addr = nullptr; //地址后面mapFile()时再计算,反正只有映射了才能真正访问到。 containerSize = container; // 容量可以先保存下来。 memSize = 0; memName = name; selfAlloc = true; return true; } void CFileMapMem2::dealloc() { std::lock_guard guard(uplocker); unmapFile(false); if(hand) { CloseHandle(hand); hand = nullptr; } selfAlloc = false; } bool CFileMapMem2::resize(size_t size) { std::lock_guard guard(uplocker); if (containerSize == 0) return false; if (size > containerSize) return false; if (size == 0) { if (memSize == containerSize) { unmapFile(true); } else if (memSize == 0) { return true; } else { return false; } } else if (size == containerSize) { if (memSize == containerSize) { return true; // nothing todo } else if (memSize == 0) { return mapFile(); } else { return false; } } else { return false; } return true; } bool CFileMapMem2::mapFile() { ASSERT(hand != nullptr); ASSERT(allMem.containerSize > 0); size_t fileSize = allMem.containerSize / 2; size_t halfSize1 = fileSize / 2; AlignDown(halfSize1, 64_K); size_t halfSize2 = allMem.containerSize/2 - halfSize1; ULARGE_INTEGER fileOffset1, fileOffset2; fileOffset1.QuadPart = 0; fileOffset2.QuadPart = halfSize1; VirtualFree(allMem.Addr, 0, MEM_RELEASE); // 释放虚拟内存空间 //但内存总容量allMem.containerSize保持不变 // 内存 映像文件 // ------------- // | 2 | // ============== --- ============== // | 1 | | 1 | // -------------- -------------- // | 2 | | 2 | // ============== --- ============== // | 1 | // -------------- Addr = (uint8_t*)MapViewOfFileEx(hand, FILE_MAP_ALL_ACCESS , fileOffset1.HighPart, fileOffset1.LowPart , fileSize, allMem.Addr + halfSize2); if(!Addr) { return false; } containerSize = memSize = allMem.containerSize/2; BottomMem.Addr = (uint8_t*)MapViewOfFileEx(hand, FILE_MAP_READ , fileOffset1.HighPart, fileOffset1.LowPart , halfSize1, allMem.Addr + halfSize2 + fileSize); TopMem.Addr = (uint8_t*)MapViewOfFileEx(hand, FILE_MAP_READ , fileOffset2.HighPart, fileOffset2.LowPart , halfSize2, allMem.Addr); if(!TopMem.Addr || !BottomMem.Addr) // 前面成功了,这里不大可能失败,稳妥起见加上 { UnmapViewOfFile(Addr); Addr = nullptr; containerSize = memSize = 0; if (TopMem.Addr) UnmapViewOfFile(TopMem.Addr); if (BottomMem.Addr) UnmapViewOfFile(BottomMem.Addr); TopMem.dealloc(); BottomMem.dealloc(); return false; }else { TopMem.containerSize = halfSize2; TopMem.memSize = 0; BottomMem.containerSize = halfSize1; TopMem.memSize = 0; } verifyMap(); return true; } void CFileMapMem2::unmapFile(bool realloc) { if (containerSize == 0 || hand == nullptr) return; if (memSize == containerSize) { if (TopMem.Addr) UnmapViewOfFile(TopMem.Addr); if (BottomMem.Addr) UnmapViewOfFile(BottomMem.Addr); if (Addr) UnmapViewOfFile(Addr); TopMem.dealloc(); BottomMem.dealloc(); Addr = nullptr; containerSize = memSize = 0; if (realloc) { _allocVirtual(allMem.Addr, allMem.containerSize, false); } else { allMem.dealloc(); } } } bool CFileMapMem2::verifyMap(bool compare_content) { ASSERT(hand != nullptr); // 验证容量 ASSERT(containerSize != 0); ASSERT(memSize = containerSize); ASSERT(containerSize = allMem.containerSize / 2); ASSERT(TopMem.containerSize + BottomMem.containerSize == containerSize); // 验证地址 ASSERT(allMem.Addr); ASSERT(TopMem.Addr == allMem.Addr); ASSERT(Addr == allMem.Addr + TopMem.containerSize); ASSERT(BottomMem.Addr = Addr + containerSize); // 验证内容 bool succ = true; if(compare_content) { uint64_t* p = (uint64_t*)(Addr); for(int i=0; ierror("memcmp(Addr, BottomMem.Addr, BottomMem.containerSize) != 0"); succ = false; } else { alog->info("底部映射验证通过"); } if (memcmp(Addr + BottomMem.containerSize, TopMem.Addr, TopMem.containerSize) != 0) { alog->error("Addr + BottomMem.containerSize, TopMem.Addr, TopMem.containerSize) != 0"); succ = false; } else { alog->info("顶部映射验证通过"); } } return succ; } std::wstring CFileMapMem2::details() { std::wstring str = fmt::format(L"虚拟内存地址:{}, 大小{}M\n", (void *)allMem.Addr, allMem.containerSize/1_M); str += fmt::format(L"数据内存地址:{}, 偏移量:{}M,大小{}M\n", (void*)Addr, (Addr - allMem.Addr) / 1_M, memSize / 1_M); str += fmt::format(L"底部内存地址:{}, 偏移量:{:4d}M,大小{:4d}M,映射到偏移量{:4d}M\n" , (void*)BottomMem.Addr, (BottomMem.Addr - allMem.Addr) / 1_M, BottomMem.containerSize / 1_M, (Addr - allMem.Addr) / 1_M); str += fmt::format(L"顶部内存地址:{}, 偏移量:{:4d}M,大小{:4d}M,映射到偏移量{:4d}M\n" , (void*)TopMem.Addr, (TopMem.Addr - allMem.Addr) / 1_M, TopMem.containerSize / 1_M, (TopMem.containerSize+ BottomMem.containerSize) / 1_M); return str; }