在 Windows 上模拟(惰性)NAND 内存
Simulating (lazy) NAND memory on Windows
我是 运行 模拟 NAND(256MB 或 1GB)的 DLL 中的固件模拟。我想避免在堆上为此分配内存,而是使用虚拟内存分配。
最初需要将内存清除为 0xFF(就像 NAND 一样)。但是我不想为该初始化付费(也不想提交未访问的页面)。所以理想情况下,它应该只在访问时分配。而且我不需要在模拟退出后保留数据。
初步想法是
虚拟分配。不确定,但考虑也许可以使用保护页面,然后在第一次访问时捕获异常。不确定 DLL 处理此类 SEH 异常是否理想?或者有更好的方法吗?
创建一个初始化为 0xFF 的大文件。然后使用写时复制映射文件视图。
有人知道是否可以创建一个带有回调的文件来提供初始数据吗?
大概考虑 1) 要走的路,但想知道这是否真的是最好的选择。
编辑:
3)我想出了另一种方法,可以避免异常处理程序,也可以避免创建一个巨大的文件:
创建一个与 dwAllocationGranularity 大小相同的文件(通常为 64KiB)。填充为 0xFF。然后使用 MapViewOfFileEx + FILE_MAP_COPY 在连续内存中创建多个写时复制视图(在初始 VirtualAlloc/VirtualFree 之后获得我们希望分配并列视图的合适基地址)。需要对此进行更全面的测试——对潜在的线程竞争略有担忧。我实际上只是在使用单个线程,但 CRT 确实也启动了一些。
这意味着任何只读取虚拟 NAND 的代码也不会导致所有页面都被提交。
是的,基本上 1 是最佳解决方案。只有我会做下一个更改 - 使用 VEH 代替 SEH - SEH 处理程序只有在你访问其中的内存时才会被调用,当如果 VEH - 访问可以是任何上下文和线程。而不是使用保护页,我最初只是保留内存区域而没有实际分配。因此对内存区域的任何访问都会导致异常,您可以在 VEH 中处理它 - 提交内存并填充 0xFF
模式。演示代码
PVOID g_NandBegin;
SIZE_T g_NandSize = 0x1000000;
LONG NTAPI Vex(::PEXCEPTION_POINTERS ExceptionInfo)
{
::PEXCEPTION_RECORD ExceptionRecord = ExceptionInfo->ExceptionRecord;
if (ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION &&
ExceptionRecord->NumberParameters > 1)
{
PVOID pv = (PVOID)ExceptionRecord->ExceptionInformation[1];
if ((ULONG_PTR)pv - (ULONG_PTR)g_NandBegin < g_NandSize)
{
SIZE_T RegionSize = 1;
if (0 <= NtAllocateVirtualMemory(NtCurrentProcess(), &pv, 0, &RegionSize, MEM_COMMIT, PAGE_READWRITE))
{
RtlFillMemoryUlong(pv, RegionSize, MAXULONG);
return EXCEPTION_CONTINUE_EXECUTION;
}
}
}
return EXCEPTION_CONTINUE_SEARCH;
}
void dc()
{
if (PVOID pv = AddVectoredExceptionHandler(TRUE, Vex))
{
if (g_NandBegin = VirtualAlloc(0, g_NandSize, MEM_RESERVE, PAGE_READWRITE))
{
ULONG seed = ~GetTickCount();
int n = 0x100;
do
{
if (*(UCHAR*)((PBYTE)g_NandBegin + (((ULONG64)RtlRandomEx(&seed) * g_NandSize) >> 32)) != 0xFF)
{
__debugbreak();
}
} while (--n);
VirtualFree(g_NandBegin, 0, MEM_RELEASE);
}
RemoveVectoredExceptionHandler(pv);
}
}
我是 运行 模拟 NAND(256MB 或 1GB)的 DLL 中的固件模拟。我想避免在堆上为此分配内存,而是使用虚拟内存分配。
最初需要将内存清除为 0xFF(就像 NAND 一样)。但是我不想为该初始化付费(也不想提交未访问的页面)。所以理想情况下,它应该只在访问时分配。而且我不需要在模拟退出后保留数据。
初步想法是
虚拟分配。不确定,但考虑也许可以使用保护页面,然后在第一次访问时捕获异常。不确定 DLL 处理此类 SEH 异常是否理想?或者有更好的方法吗?
创建一个初始化为 0xFF 的大文件。然后使用写时复制映射文件视图。 有人知道是否可以创建一个带有回调的文件来提供初始数据吗?
大概考虑 1) 要走的路,但想知道这是否真的是最好的选择。
编辑: 3)我想出了另一种方法,可以避免异常处理程序,也可以避免创建一个巨大的文件: 创建一个与 dwAllocationGranularity 大小相同的文件(通常为 64KiB)。填充为 0xFF。然后使用 MapViewOfFileEx + FILE_MAP_COPY 在连续内存中创建多个写时复制视图(在初始 VirtualAlloc/VirtualFree 之后获得我们希望分配并列视图的合适基地址)。需要对此进行更全面的测试——对潜在的线程竞争略有担忧。我实际上只是在使用单个线程,但 CRT 确实也启动了一些。 这意味着任何只读取虚拟 NAND 的代码也不会导致所有页面都被提交。
是的,基本上 1 是最佳解决方案。只有我会做下一个更改 - 使用 VEH 代替 SEH - SEH 处理程序只有在你访问其中的内存时才会被调用,当如果 VEH - 访问可以是任何上下文和线程。而不是使用保护页,我最初只是保留内存区域而没有实际分配。因此对内存区域的任何访问都会导致异常,您可以在 VEH 中处理它 - 提交内存并填充 0xFF
模式。演示代码
PVOID g_NandBegin;
SIZE_T g_NandSize = 0x1000000;
LONG NTAPI Vex(::PEXCEPTION_POINTERS ExceptionInfo)
{
::PEXCEPTION_RECORD ExceptionRecord = ExceptionInfo->ExceptionRecord;
if (ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION &&
ExceptionRecord->NumberParameters > 1)
{
PVOID pv = (PVOID)ExceptionRecord->ExceptionInformation[1];
if ((ULONG_PTR)pv - (ULONG_PTR)g_NandBegin < g_NandSize)
{
SIZE_T RegionSize = 1;
if (0 <= NtAllocateVirtualMemory(NtCurrentProcess(), &pv, 0, &RegionSize, MEM_COMMIT, PAGE_READWRITE))
{
RtlFillMemoryUlong(pv, RegionSize, MAXULONG);
return EXCEPTION_CONTINUE_EXECUTION;
}
}
}
return EXCEPTION_CONTINUE_SEARCH;
}
void dc()
{
if (PVOID pv = AddVectoredExceptionHandler(TRUE, Vex))
{
if (g_NandBegin = VirtualAlloc(0, g_NandSize, MEM_RESERVE, PAGE_READWRITE))
{
ULONG seed = ~GetTickCount();
int n = 0x100;
do
{
if (*(UCHAR*)((PBYTE)g_NandBegin + (((ULONG64)RtlRandomEx(&seed) * g_NandSize) >> 32)) != 0xFF)
{
__debugbreak();
}
} while (--n);
VirtualFree(g_NandBegin, 0, MEM_RELEASE);
}
RemoveVectoredExceptionHandler(pv);
}
}