发布时间:2026/8/16 1:10:40
ReactOS 图形系统分析(23):引擎内存管理 — mem.c ReactOS 图形系统分析23引擎内存管理 — mem.c1. 概述mem.c位于win32ss/gdi/eng/mem.c文件头自述 “GDI Driver Memory Management Functions”实现 GDI 引擎的内存管理服务族——既是引擎内部各模块的通用分配入口也是 DDI 契约中供显示/字体/打印驱动调用的内存服务win32k.sys 导出。按能力分三类┌─────────────────────────────────────────────────────────────┐ │ gdi/eng/mem.c本分析对象 │ │ A. 内核池分配 │ │ EngAllocMemPaged/NonPaged 清零/ EngFreeMem │ │ B. 用户虚拟内存分配 │ │ EngAllocUserMem / EngFreeUserMemZwAllocateVirtMem │ │ C. 用户内存安全锁定防内核使用中被用户释放 │ │ EngSecureMem / EngSecureMemForRead / EngUnsecureMem │ │ HackSecureVirtualMemory / HackUnsecureVirtualMemory │ │ MDL 探测锁定供内核访问用户缓冲区 │ ├─────────────────────────────────────────────────────────────┤ │ 底层ExAllocatePoolWithTag / Zw(Alloc|Free)VirtualMemory / │ │ MmSecureVirtualMemory / IoAllocateMdlMmProbeAndLock │ └─────────────────────────────────────────────────────────────┘重要观察EngAllocMem是全引擎最通用的分配服务——全树 32 个文件、65 处调用eng/ntgdi/字体驱动/显示驱动/DirectX 全在用是《分析_4》2.x 各对象SURFACE/EBRUSHOBJ/EXLATEOBJ 等与《分析_18/19》等文件分配内存的共同底层。2. 内核池分配 — EngAllocMem / EngFreeMemPVOIDEngAllocMem(ULONG fl,ULONG cjMemSize,ULONG ulTag){pvBaseAddressExAllocatePoolWithTag((flFL_NONPAGED_MEMORY)?NonPagedPool:PagedPool,cjMemSize,ulTag);if(pvBaseAddressNULL)returnNULL;if(flFL_ZERO_MEMORY)RtlZeroMemory(pvBaseAddress,cjMemSize);returnpvBaseAddress;}VOIDEngFreeMem(PVOID pvBaseAddress){if(pvBaseAddress)// Windows 允许传 NULLExFreePoolWithTag(pvBaseAddress,0);// tag 0 ExFreePool不校验 tag}标志[winddi.h](file:///d:/reactos/sdk/include/psdk/winddi.h#L1358-L1359)标志含义FL_ZERO_MEMORY(0x1)分配后清零RtlZeroMemoryFL_NONPAGED_MEMORY(0x2)从NonPagedPool分配缺省为 PagedPool设计要点默认分页池大部分 GDI 对象可分页换出需要锁路径/中断上下文时显式FL_NONPAGED_MEMORY如信号量 ERESOURCE见《分析_20》。ulTag用于池标签调试WinDbg!pool可看归属EngFreeMem用 tag 0 等价ExFreePool不校验标签。与EngAllocSectionMem《分析_21》会话空间大块共享分配形成小对象用池 / 大共享用节的分层。3. 用户虚拟内存分配 — EngAllocUserMem / EngFreeUserMemPVOIDEngAllocUserMem(SIZE_T cjMemSize,ULONG ulTag){StatusZwAllocateVirtualMemory(NtCurrentProcess(),pvBaseAddress,0,cjMemSize,MEM_COMMIT|MEM_RESERVE,PAGE_READWRITE);.../* TODO: Add allocation info to AVL tree (stored inside W32PROCESS) */}VOIDEngFreeUserMem(PVOID pvBaseAddress){ZwFreeVirtualMemory(NtCurrentProcess(),pvBaseAddress,cjSize,MEM_RELEASE);}语义在当前进程的用户地址空间一次性保留提交一段读写内存用于内核替用户分配 GDI 位图缓冲等。树内消费者消费者用途[surface.c](file:///d:/reactos/win32ss/gdi/eng/surface.c#L238)BMF_USERMEM位图的像素内存CreateBitmap 用户内存模式[dibobj.c](file:///d:/reactos/win32ss/gdi/ntgdi/dibobj.c#L2017)位图信息查询/转换的临时用户缓冲bm.bmBits已知 TODO源码标注分配信息应记入 W32PROCESS 的 AVL 树以便进程退出时清理当前未实现若进程中途退出该用户内存可能泄漏/由 VAD 回收。4. 用户内存安全锁定 — EngSecureMem / EngSecureMemForRead / EngUnsecureMemWindows 语义当内核正在使用一块用户模式内存如 DIB Section 位图、系统调用传入的缓冲时必须锁定它——否则用户线程可能并发释放该内存VirtualFree/退出进程内核继续写就崩溃。EngSecureMem返回不透明句柄用毕EngUnsecureMem解锁。ReactOS 现状关键三个函数的真实现被{// HACK!!!块短路真正的MmSecureVirtualMemory分支不可达HANDLEEngSecureMem(PVOID Address,ULONG Length){{// HACK!!!_SEH2_TRY{ProbeForWrite(Address,Length,1);}// 仅做可写性探测_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER){_SEH2_YIELD(returnNULL);}_SEH2_END;return(HANDLE)-1;// ← 恒返回 -1假句柄}returnMmSecureVirtualMemory(Address,Length,PAGE_READWRITE);// 不可达}VOIDEngUnsecureMem(HANDLE Mem){if(Mem(HANDLE)-1)return;// 假句柄直接跳过MmUnsecureVirtualMemory((PVOID)Mem);}EngSecureMem只做ProbeForWrite校验后返回假句柄-1无真正锁定EngSecureMemForRead类似逐页读探测。EngUnsecureMem遇-1直接返回——成对调用下行为等价只探测不锁定。这意味着 ReactOS 当前没有真正防止用户释放正在使用内存的机制属安全缺口/简化。消费者[dibobj.c](file:///d:/reactos/win32ss/gdi/ntgdi/dibobj.c#L1166)DIB 位图锁定、[mapping.c](file:///d:/reactos/win32ss/gdi/eng/mapping.c#L58)EngMapSectionView 后锁定视图见《分析_21》。5. MDL 锁定 — HackSecureVirtualMemory / HackUnsecureVirtualMemory供内核访问用户缓冲区如 DIB 引擎读写用户位图的MDL 探测映射辅助PVOIDHackSecureVirtualMemory(PVOID Address,SIZE_T Size,ULONG ProbeMode,PVOID*SafeAddress){pmdlIoAllocateMdl(Address,Size,FALSE,TRUE,NULL);MmProbeAndLockPages(pmdl,UserMode,Operation);// 探测锁定用户页*SafeAddressMmGetSystemAddressForMdlSafe(pmdl,NormalPagePriority);returnpmdl;// 返回 MDL 作为句柄}VOIDHackUnsecureVirtualMemory(PVOID SecureHandle){MmUnlockPages(pmdl);// 解锁IoFreeMdl(pmdl);// 释放 MDL}流程IoAllocateMdl→MmProbeAndLockPagesSEH 保护探测失败返回 NULL→MmGetSystemAddressForMdlSafe得到系统空间映射地址内核即可直接读写用户缓冲是 4.3 节只探测不锁定的安全替代方案真正的页锁定 系统地址映射声明于 [ntgdi/misc.h](file:///d:/reactos/win32ss/gdi/ntgdi/misc.h#L29-L30)。6. 导出与消费路径6.1 win32k.spec 导出[win32k.spec](file:///d:/reactos/win32ss/win32k.spec) stdcall EngAllocMem(long long long) # L9 stdcall EngAllocPrivateUserMem(ptr ptr long) # L10本文件未实现spec 保留 stdcall EngAllocUserMem(ptr long) # L12 stdcall EngFreeMem(ptr) # L57 stdcall EngFreePrivateUserMem(ptr ptr) # L59本文件未实现 stdcall EngFreeUserMem(ptr) # L61 stdcall EngSecureMem(ptr long) # L118 stdcall EngUnsecureMem(ptr) # L1406.2 其他导出路径路径内容[dxgthk.spec](file:///d:/reactos/win32ss/reactx/dxgthk/dxgthk.spec#L3-L14)重新导出EngAllocUserMem/EngFreeUserMem用户态 DirectX 驱动用[win32u/umpdstubs](file:///d:/reactos/win32ss/gdi/eng/umpdstubs.c#L9)NtGdiUMPDEngFreeUserMemUMPD 用户内存释放6.3 调用链示例EngAllocMem65 处调用eng/ntgdi/驱动ExAllocatePoolWithTagPaged/NonPagedEngAllocUserMemZwAllocateVirtualMemory用户地址空间EngSecureMemProbeForWrite 假句柄-1真实锁定未启用HackSecureVirtualMemoryMDL 探测锁定 系统地址映射7. 与《分析_4》标注对照标注核对《_4》第 43 行mem.c 「引擎内存分配」准确。补充三类能力内核池/用户内存/安全锁定EngAllocMem为全引擎通用分配入口EngSecureMem当前为只探测不锁定的 HACK 简化8. 源码索引文件关键内容[mem.c](file:///d:/reactos/win32ss/gdi/eng/mem.c)EngAllocMem/FreeMem、EngAllocUserMem/FreeUserMem、EngSecureMem(ForRead)/UnsecureMem、HackSecure/UnsecureVirtualMemory[winddi.h](file:///d:/reactos/sdk/include/psdk/winddi.h#L1358-L1359)FL_ZERO_MEMORY / FL_NONPAGED_MEMORY[misc.h](file:///d:/reactos/win32ss/gdi/ntgdi/misc.h#L29-L30)Hack* 声明[surface.c](file:///d:/reactos/win32ss/gdi/eng/surface.c#L238)BMF_USERMEM 位图用 EngAllocUserMem[mapping.c](file:///d:/reactos/win32ss/gdi/eng/mapping.c#L58)EngMapSectionView 用 EngSecureMem《分析_21》[dibobj.c](file:///d:/reactos/win32ss/gdi/ntgdi/dibobj.c#L1166)DIB 位图锁定 / 用户缓冲分配关联模块大块共享分配EngAllocSectionMem《分析_21》mapping.c会话空间分配对象SURFACE/EBRUSHOBJ/EXLATEOBJ/PATHOBJ 等《分析_4/17/18/19》内存标签GDITAG_* 系列ExAllocatePoolWithTag9. 各函数使用说明9.1 EngAllocMem — 分配内核池内存最常用PVOIDEngAllocMem(ULONG fl,ULONG cjMemSize,ULONG ulTag);参数说明fl标志FL_ZERO_MEMORY清零/FL_NONPAGED_MEMORY非分页池可 OR 组合cjMemSize请求字节数ulTag4 字节池标签如rusG用于调试!pool归类使用要点默认是分页池——只有锁路径/中断/DMA 等必须常驻的场景才加FL_NONPAGED_MEMORY例信号量 ERESOURCE 见《分析_20》。返回 NULL 表示分配失败必须判空再使用。要求清零内存时传FL_ZERO_MEMORY省去手动memset。释放必须用EngFreeMem配 tag 0不要直接用ExFreePool会丢失允许 NULL的语义约定。典型用法PBRUSH pbrEngAllocMem(FL_ZERO_MEMORY,sizeof(BRUSH),GDITAG_BRUSH);if(!pbr)returnNULL;// 失败处理...EngFreeMem(pbr);// 释放NULL 安全9.2 EngFreeMem — 释放 EngAllocMem 分配的内存VOIDEngFreeMem(PVOID pvBaseAddress);使用要点允许传 NULL与 Windows 一致无需外层判空。内部用 tag 0 调用ExFreePoolWithTag等价ExFreePool不校验标签。只能释放EngAllocMem或驱动直接用ExAllocatePoolWithTag分配的内核池内存不能释放用户内存或节映射内存。9.3 EngAllocUserMem — 在当前进程用户空间分配内存PVOIDEngAllocUserMem(SIZE_T cjMemSize,ULONG ulTag);使用要点分配在当前进程的用户地址空间ZwAllocateVirtualMemoryMEM_COMMIT|MEM_RESERVEPAGE_READWRITE内核可直接写用户态可读。典型用途为BMF_USERMEM位图提供像素缓冲、为位图信息查询提供临时缓冲。释放必须用EngFreeUserMem。已知局限源码 TODO分配记录未纳入 W32PROCESS 的 AVL 树——若分配后进程异常退出该内存靠进程 VAD 回收引擎不主动跟踪。典型用法pvBitsEngAllocUserMem(cjBits,0);if(!pvBits)returnFALSE;.../* 内核填像素 */EngFreeUserMem(pvBits);/* 用毕释放 */9.4 EngFreeUserMem — 释放用户空间内存VOIDEngFreeUserMem(PVOID pvBaseAddress);使用要点对应EngAllocUserMem的逆操作ZwFreeVirtualMemoryMEM_RELEASE。注意EngFreeUserMem不判 NULL与EngFreeMem不同——调用前需自行确认指针有效。只释放由内核替当前进程分配的用户内存不要用来释放应用自己VirtualAlloc的内存。9.5 EngSecureMem — 锁定用户内存当前为 HACK 简化版HANDLEEngSecureMem(PVOID Address,ULONG Length);使用要点语义防止内核使用期间用户并发释放该内存返回不透明句柄用毕EngUnsecureMem解锁。ReactOS 现状只做ProbeForWrite可写性探测恒返回假句柄(HANDLE)-1真正的MmSecureVirtualMemory被{// HACK!!!块短路没有真正锁定。因此安全校验逻辑要自己写若后续代码依赖内存已被锁定当前实现并不保证——需自行加锁或改用HackSecureVirtualMemory。返回值判 NULL/-1均表示失败探测不通过。典型用法hSecureEngSecureMem(pjBits,cjMaxBits);if(!hSecure)returnFALSE;/* 探测失败 */.../* 使用 pjBits */EngUnsecureMem(hSecure);/* 假句柄 -1 会被直接跳过 */9.6 EngSecureMemForRead — 锁定只读用户内存HANDLEEngSecureMemForRead(PVOID Address,ULONG Length);使用要点与EngSecureMem同构但做的是逐页读探测ProbeForRead 每页读一次语义为内核将只读该缓冲。同样返回假句柄-1真实锁定未启用HACK。使用场景只读用户缓冲如 GetBitmapBits 的输入参数校验。9.7 EngUnsecureMem — 解除锁定VOIDEngUnsecureMem(HANDLE Mem);使用要点与EngSecureMem/EngSecureMemForRead成对使用。遇假句柄(HANDLE)-1直接返回HACK 配套逻辑真实句柄才调用MmUnsecureVirtualMemory。不要对 NULL 或未加锁的地址调用无防护成对性由调用方保证。9.8 HackSecureVirtualMemory — 用 MDL 锁定用户缓冲并映射到系统空间PVOIDHackSecureVirtualMemory(PVOID Address,SIZE_T Size,ULONG ProbeMode,PVOID*SafeAddress);参数说明Address/Size用户缓冲地址与大小ProbeModePAGE_READONLY读锁/PAGE_READWRITE读写锁SafeAddress输出可被内核直接访问的系统空间映射地址使用要点返回值为PMDL作为后续释放的句柄不是映射地址——映射地址在SafeAddress输出。流程IoAllocateMdl→MmProbeAndLockPagesSEH 保护失败返回 NULL→MmGetSystemAddressForMdlSafe。用毕必须调用HackUnsecureVirtualMemory释放解锁 释放 MDL。这是当前引擎里真正能锁定用户页的方案优于 9.5 的探测式 HACK适合 DIB 引擎需要长时间读写用户位图的场景。典型用法pmdlHackSecureVirtualMemory(pvUserBits,cbSize,PAGE_READWRITE,pvSafe);if(!pmdl)returnFALSE;memcpy(pvSafe,...);/* 内核通过系统地址访问 */HackUnsecureVirtualMemory(pmdl);9.9 HackUnsecureVirtualMemory — 释放 MDL 锁定VOIDHackUnsecureVirtualMemory(PVOID SecureHandle);使用要点参数是HackSecureVirtualMemory返回的 PMDL。依次MmUnlockPagesIoFreeMdl成对释放。必须与 9.8 一一对应过早释放会导致内核访问已解锁页面的错误。本文档基于 ReactOS 源代码win32ss/gdi/eng/mem.c及关联模块分析2026 年 8 月

相关新闻

2026/8/16 1:05:40

给 Codex 分加载任务,我会先划清页面、按钮和局部三层边界

上一篇说过,列表页的“忙”不是一个,是几个。这一篇把这些“忙”落成三层,每一层有自己的归属和关门条件。分层以后,交给 Codex 的任务就从“加个 loading”变成“给这个页面找出几个等待,每个等待归谁管”。 分层不是…

2026/8/16 1:05:40

Codex 写列表时,加载状态为什么不是加一个 loading 就够

“loading 加了吗?加了。” 这是交接任务时最常听到的一句答复。但列表页的“加载中”从来不是一件事。首次进入页面要等首屏数据,翻页和查询要等表格数据,点保存要等提交结果。这三个“忙”发生在不同时刻,持续不同时间,也由不同动作结束。 如果它们共用一个布尔值,麻…

2026/8/16 1:00:39

怀旧游戏神器!安卓Java模拟器重温经典

它是一款java模拟器安卓版, 属java游戏模拟平台, 深受小朋友们的喜爱, 它能汇集海量游戏资源, 游戏类型繁多, 能靠众多游戏类型满足每位玩家需求, 它卓越兼容性确保游戏流畅运行, 给用户带来极致游戏享受, 令小编佩服的是, 该软件凭借现代硬件技术, 对游戏图形做了全面优化, 让…

2026/8/16 2:06:08

OpenClaw AI智能体:从Docker部署到技能开发,打造你的自动化副手

1. 从“玩具”到“副手”:OpenClaw的定位与核心价值最近在折腾本地AI智能体,OpenClaw这个名字出现的频率越来越高。一开始我以为它又是一个需要复杂配置、写大量代码的“开发者玩具”,但真正上手后,发现它的设计理念非常有意思&am…

2026/8/16 2:06:08

Windows纯净版MinGW-w64安装配置指南:从下载到VSCode集成

1. 项目概述:为什么我们需要一个“干净”的MinGW-w64?在Windows上进行C或C开发,尤其是涉及到一些开源库的编译时,一个绕不开的工具就是MinGW-w64。你可能听说过它,也可能被它复杂的安装过程劝退过。网上教程五花八门&a…

2026/8/16 2:06:08

OpenClaw五分钟部署实战:四年AI工程避坑与生产化指南

1. 项目概述:从“5分钟部署”到四年实战沉淀看到“5分钟搞定OpenClaw部署”这个标题,很多朋友的第一反应可能是怀疑,或者觉得这又是一个吸引眼球的噱头。作为一个在AI应用开发和部署一线摸爬滚打了四年的从业者,我完全理解这种感受…

2026/8/16 2:06:08

10GW算力集群核心技术解析:从异构计算到分布式训练实战

最近在技术圈里,SpaceXAI 这个名字被频繁提及,尤其是在其宣布“目标明年实现 10GW 算力集群”之后。对于开发者而言,这不仅仅是一条商业新闻,更是一个强烈的技术信号:大规模、高密度的算力集群正在从云端走向更广泛的产…

2026/8/16 2:01:08

XUnity.AutoTranslator翻译服务横评:游戏本地化如何选择最佳API

1. 项目概述:为什么游戏翻译服务的选择如此重要?如果你是一个热衷于体验全球各地独立游戏或视觉小说的玩家,或者是一位需要将作品本地化推向国际市场的Unity开发者,那么“翻译”这个环节你一定绕不过去。手动替换文本?…

2026/8/16 0:00:35

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/16 0:00:36

工业传感器与变送器详解:序章 从物理世界到工业数据

序章 从物理世界到工业数据 ——重新认识工业传感器与变送器 工业自动化系统正变得日益复杂。今天的工业现场早已不是简单的控制回路,而是由多层技术共同构成的立体体系:PLC、DCS、SCADA、MES、工业互联网、边缘计算与人工智能。控制系统可以执行复杂算法,工业网络可以实现…

2026/8/16 0:00:35

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/16 0:00:36

工业传感器与变送器详解:序章 从物理世界到工业数据

序章 从物理世界到工业数据 ——重新认识工业传感器与变送器 工业自动化系统正变得日益复杂。今天的工业现场早已不是简单的控制回路,而是由多层技术共同构成的立体体系:PLC、DCS、SCADA、MES、工业互联网、边缘计算与人工智能。控制系统可以执行复杂算法,工业网络可以实现…

2026/8/15 9:46:39

实测才敢推 AI论文网站 2026最新测评与推荐

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一、综…

2026/8/15 4:56:16

2026必备!AI论文网站测评:最新推荐与深度对比

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

2026/8/15 9:46:30

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…