在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>比較好.
這裡說的,和看到的可能並不是真相。還請達人指點一二。