在 Windows 上模拟(惰性)NAND 内存

Simulating (lazy) NAND memory on Windows

我是 运行 模拟 NAND(256MB 或 1GB)的 DLL 中的固件模拟。我想避免在堆上为此分配内存,而是使用虚拟内存分配。

最初需要将内存清除为 0xFF(就像 NAND 一样)。但是我不想为该初始化付费(也不想提交未访问的页面)。所以理想情况下,它应该只在访问时分配。而且我不需要在模拟退出后保留数据。

初步想法是

  1. 虚拟分配。不确定,但考虑也许可以使用保护页面,然后在第一次访问时捕获异常。不确定 DLL 处理此类 SEH 异常是否理想?或者有更好的方法吗?

  2. 创建一个初始化为 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);
    }
}