|
Modules, Processes, Threads(1)
Windows 95 System Programming SECRETsXML:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> (Windows 95 系统程序设计 大奥秘) 原著:Matt Pietrek 笔记:Simon wan 模块、行程、执行绪(Modules, Processes, Threads)Win32 模块(Modules)一个 Win32 模块代表的是一个被 Win32 载入器载入的EXE 或DLL 的程序代码、数据、资源。因此,内存中的一个模块都对应到磁盘中的一个档案。EXE 或 DLL 本身并不是模块。是加载器把它们读进内存并产生出模块。 应用程序使用 HMODULEs 来代表被加载的模块。Win32 并没有所谓的节区,所以需要一些其它方法来参考被加载模块。微软的作法是让一个 HMODULE 成为Win32 载入器映像 PE 档案时的起始线性地址。例如,大部份 EXE 程序被加载于地址 0x400000(4MB)处,所以它们的HMODULE 就是 0x400000。是的,这意味着多个 EXE 同时执行时,拥有相同的HMODULE。这不是问题,因为Windows 95 和 NT 为每一个行程维护了一个分离地址空间。 Win32 要求每一个行程有自己的模块串行。如果模块没有隐式联结(implicitly link)DLLs,或说它是以 LoadLibrary 加载 DLLs,行程就没有办法在内存中看到那些DLL 模块 -- 即使其它行程加载了它们也一样。这在 Win16 是相当不同的,Win16的每一个被加载模块可以被所有其它行程看到,不论它们有没有参考到该 DLL。 虽然,「每一个 Win32 行程有自己的模块串行」这件事对于安全防护性和强固性有好的影响,从「利用可共享之程序代码和资源以节省空间」的角度来看却不怎么好。毕竟,如果你执行三份 WINHELP,WINHELP 的码不应该被加载三次,对不对? KERNEL32 必须面对一个费力的选择。从应用程序眼光来看,每一个行程有自己的模块串行是不错;但从 KERNEL32 的角度来看,单一模块串行比较容易达到程序代码和资源的共享。只要有一个新的行程开始执行,或一个新的 DLL 被加载,KERNEL32 就可以快速检查独一无二的全域性模块串行,看看那个 EXE 或 DLL 是否已经加载?如果是,KERNEL32 就简单地改变其计数值。如果不是,KERNEL32 才需要在内存中为它产生新的模块。 KERNEL32 利用两个数据结构来维护一个全域性模块串行,并且使它看起来好像每一个行程有自己的一个串行。第一个是数据结构 IMTE(Internal Module Table Entry),第二个数据结构是 MODREF。 IMTEs(Internal Module Table Entries)如图3-1 所示,全域性 KERNEL32 模块串行其实只不过是 IMTEs 指针所组成的数组。稍后所列的伪码中,我将以 pModuleTableArray 代表指向此一数组的指针。这块数组所使用的内存是从 KERNEL32 heap 中配置而来,那是一般的 HeapAlloc 所获得的一个区块。当新模块被加载或踢出内存,KERNEL32 利用 HeapReAlloc 动态扩张或缩减数组大小。当 KERNEL32 产生一个新的 IMTE , 它会搜寻pModuleTableArray 中的空白元素;找到了一个,就把 IMTE 指标放进去。这个元素的数组索引值稍后在我们搜寻 MODREFs 时将有吃重的演出。pModuleTableArray 的第一个元素(索引为 0)用来表示KERNEL32.DLL 模块。 让我快速叙述一下重点。pModuleTableArray 中的每一个非零元素都代表系统中一个被载入的 EXE 或 DLL。每一个这样的元素都是一个 IMTE 指标(在伪码中我以 PIMTE表示)
|