Files
GdCpp12/source/Mem/CMem.cpp

478 lines
12 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "pch.h"
#include "Mem\CMem.h"
#include <Windows.h>
#include "AlignSize.h"
//#include "CSystemSemaphore.h"
#include "aLog.h"
// 静态成员变量
size_t CMemUsage::totalContainerSize=0;
size_t CMemUsage::totalMemSize=0;
std::list<CMemUsage*> 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<uint8_t*>(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<uint8_t*>(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<uint8_t*>(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<uint8_t*>(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; i<containerSize/8; i++)
{
*p++ = i;
}
if (memcmp(Addr, BottomMem.Addr, BottomMem.containerSize) != 0)
{
alog->error("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;
}