[继续讨论]关于Windows PE和.net assembly的加载
在firelong写的关于近期C#大论战的回应的评论中有许多观点。有些话题当时没有看清楚。后来抽时间看了一下。那些评论里面的观点实在太多,没有办法一一验证。我只谈我的发现。
1. SizeOfImage对Windows PE内存加载的影响
我开始建立起来的概念是Windows PE都会全部加载进内存执行。当那个评论中有人提到了RAR自解压EXE。我当时是想当然地认为RAR自解压EXE同样也会全部加载进内存. 后来经其他人的指出,还有做试验,证实即使物理内存不够大同时没有页面文件的情况下也能解压一个很大的文件。我真有点想不透是什么原因。当时有一个网友gussing提到了SizeOfImage参数, 让我有点启发。后来经过进一步的查看,才知道RAR自解压EXE是Windows PE的一种特例。这个特别之处就在SizeOfImage参数上。用Windbg装入一个35M的RAR自解压EXE, 然后用RAMMap查看其物理内存占用.发现其内存占用大约是0x00020000. 再用CFF explorer VII查看其PE文件, 果然SizeOfImage字段是0x00020000.
其他的Windows PE文件呢,如ntdll.dll,其SizeOfImage是0x00127000, 其文件大小是1,202,168, 十六进制是0x1257F8. 看来SizeOfImage刚好和文件大小相匹配,刚刚足以装下这个ntdll.dll本身。再看Windows文件夹下许多其他的EXE/DLL, 很多都是SizeOfImage与其文件大小刚好相匹配。
综上所述, SizeOfImage决定了PE加载到内存里的大小。
2. Windoes PE是用File mapping加载的
网友Ivony一直说这个观点。现在经过证实了。上一张图来说明问题:
图一
这个图二可以帮组你理解File mapping是怎么运作的。
图二
3. 许多Windows PE是全部加载进内存的
上面那个图一已经告诉我们MSPAINT.EXE在内存里的物理地址,而且上下卷动一下,你会发现每个物理内存页都是Active. 就是都在内存里。当然了,不是所有的Windows PE都全部加载进内存。因为这受SizeOfImage参数影响。
4. .net assembly(即.net PE)也是全部加载进内存的
首先.net PE的SizeOfImage也是与其文件大小相匹配的。这是前提。接下来看看实际的证据:
我的.net PE文件大小27M左右:
图三
运行之后,用RAMMap查看物理内存页状态:
图四
图五
这个文件很大。内存页有很多。上下翻动看了一下,除了前面有一些Standby之外,大部分是Active,就是说大部分在内存里面. 但是为什么会有Standby呢?开始还是不太明白。直到我看到了VMMap给出的图:
图六
图七
看到.net PE的这些节没有: header, .text, .rsrc, .reloc, 还有标记为Reserved的节没有。对比了非托管的Windows PE和.net PE, 在内存映像上是有差别的。非托管的Windows PE基本是全部装入内存,而.net PE的内存映像总要空几段。在header和.text之间要空一段,.rsrc和.reloc之间要空一段。
5. 另外一个问题, Working set 为什么只有7-8M大呢?
这个.net PE大部分实际上已经进了内存。但是为什么这个进程的Working set为什么只有7-8M大呢?
想起了Working set的定义: The working set of a program is a collection of those pages in its virtual address space that have been recently referenced.
然后又想起了图二。有点明白是什么回事情了。那就是每个进程建立的File view不一样。这个File View对Working set的大小有影响。
还是系统地读<Windows Internals>比较好.
这里说的,和看到的可能并不是真相。还请达人指点一二。