<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    so true

    心懷未來(lái),開(kāi)創(chuàng)未來(lái)!
    隨筆 - 160, 文章 - 0, 評(píng)論 - 40, 引用 - 0
    數(shù)據(jù)加載中……

    進(jìn)程的虛擬地址空間

    昨晚看到了深夜,終于對(duì)進(jìn)程的虛擬地址空間有了個(gè)大致的了解,很激動(dòng),也很欣慰?;仡^想來(lái),一個(gè)程序員,真的應(yīng)該知道這些知識(shí),否則還真不太稱(chēng)職。
    首先告訴大家,我后面提到的這些知識(shí)在《windows核心編程》中都有,強(qiáng)烈建議大家把這本書(shū)翻翻,我相信會(huì)對(duì)你的編程境界拔高好幾個(gè)層次的??墒俏易罱鼪](méi)那么多時(shí)間,因此就只能了解個(gè)大概,然后等今后閑暇時(shí)再看這本書(shū)吧。
    昨天我媳婦還反復(fù)和我說(shuō):學(xué)東西必須要有選擇,不能對(duì)IT行業(yè)的所有知識(shí)亂學(xué)習(xí),而且不要學(xué)那種實(shí)際意義不大的知識(shí)或是容易被淘汰的知識(shí)。其實(shí)她說(shuō)的蠻對(duì)的,但是我要說(shuō),有關(guān)《windows核心編程》里的知識(shí)永遠(yuǎn)都不會(huì)過(guò)時(shí),因?yàn)樗秩氲降讓雍蛢?nèi)部了,就像C++,你覺(jué)得會(huì)過(guò)時(shí)嗎?就像windows永遠(yuǎn)不會(huì)被淘汰一樣,呵呵。

    下面我就來(lái)粗略的說(shuō)說(shuō)我了解的一些基本知識(shí):
    32位機(jī)器,每個(gè)程序有4G的虛擬地址空間。大致分為4塊,從低地址到高地址依次是:NULL區(qū),用戶區(qū),隔離區(qū),核心區(qū)。用戶私有的數(shù)據(jù)都在用戶區(qū)(當(dāng)然這個(gè)區(qū)里又可以細(xì)分,其中也包括一部分可以共享的內(nèi)容),系統(tǒng)內(nèi)核等東西都在核心區(qū)。總體來(lái)說(shuō),A進(jìn)程的虛擬地址空間中的內(nèi)容和B進(jìn)程相比,只有各自的用戶區(qū)不一致。通常用戶區(qū)中,進(jìn)程又會(huì)將exe文件(由頭數(shù)據(jù)和段數(shù)據(jù)組成)中定義的代碼段、堆棧段、數(shù)據(jù)段等各個(gè)段映射到用戶區(qū)的特定不同部位。對(duì)于這部分區(qū)域,用戶需要用VirtualAlloc先為自己預(yù)留后再提交,最后在自己的頁(yè)面被cpu訪問(wèn)時(shí)再?gòu)膃xe映像中將數(shù)據(jù)加載到主存,然后將虛擬地址映射為主存的物理地址?;旧线@樣就可以了,至于系統(tǒng)如何進(jìn)行頁(yè)面的管理以及地址映射如何實(shí)現(xiàn)等細(xì)節(jié)請(qǐng)大家再參考別的文獻(xiàn)。

    我本以為很復(fù)雜呢,結(jié)果寫(xiě)出來(lái),就這么一小段,呵呵,看來(lái)是高估了自己理解的東西了,呵呵。

    下面貼出我看的一些資料:

    虛擬存儲(chǔ)器是一個(gè)抽象概念,它為每一個(gè)進(jìn)程提供了一個(gè)假象,好像每個(gè)進(jìn)程都在獨(dú)占的使用主存。每個(gè)進(jìn)程看到的存儲(chǔ)器都是一致的,稱(chēng)之為虛擬地址空間。

         每個(gè)進(jìn)程看到得虛擬地址空間有大量準(zhǔn)確定義的區(qū)(area)構(gòu)成,每個(gè)區(qū)都有專(zhuān)門(mén)的功能。從最低的地址看起

    • 程序代碼和數(shù)據(jù):代碼是從同一固定地址開(kāi)始,緊接著的是和C全局變量相對(duì)應(yīng)的數(shù)據(jù)區(qū)。 (應(yīng)該就是所謂的靜態(tài)存儲(chǔ)空間)
    • 堆:代碼和數(shù)據(jù)區(qū)后緊隨著的是運(yùn)行時(shí)堆。作為調(diào)用mallocfree這樣的C標(biāo)準(zhǔn)庫(kù)函數(shù),堆可以在運(yùn)行時(shí)動(dòng)態(tài)的擴(kuò)展和收縮。(應(yīng)該就是所謂的動(dòng)態(tài)存儲(chǔ)區(qū))
    • 共享庫(kù):在地址空間的中間附近是一塊用來(lái)存放像C標(biāo)準(zhǔn)庫(kù)和數(shù)學(xué)庫(kù)這樣共享庫(kù)的代碼和數(shù)據(jù)的區(qū)域。(C標(biāo)準(zhǔn)庫(kù)函數(shù)的指令,連接階段把他們加入到編譯后的程序)
    • :位于用戶虛擬地址空間頂部的是用戶,編譯器用它來(lái)實(shí)現(xiàn)函數(shù)調(diào)用。和堆一樣每次我們從函數(shù)返回時(shí),就會(huì)收縮。
    • 內(nèi)核虛擬存儲(chǔ)器:內(nèi)核是操作系統(tǒng)總是駐留在存儲(chǔ)器中的部分。地址空間頂部的四分之一部分是為內(nèi)核預(yù)留的。(系統(tǒng)函數(shù)?這里說(shuō)的UNIX系統(tǒng),不知道windows下是不是這樣的?)

         今天大多數(shù)計(jì)算機(jī)的字長(zhǎng)都是32字節(jié),這就限制了虛擬地址空間為4千兆字節(jié)(4GB

    引言

      Windows的內(nèi)存結(jié)構(gòu)是深入理解Windows操作系統(tǒng)如何運(yùn)作的關(guān)鍵之所在,通過(guò)對(duì)內(nèi)存結(jié)構(gòu)的認(rèn)識(shí)可清楚地了解諸如進(jìn)程間數(shù)據(jù)的共享、對(duì)內(nèi)存進(jìn)行有效的管理等問(wèn)題,從而能夠在程序設(shè)計(jì)時(shí)使程序以更加有效的方式運(yùn)行。Windows操作系統(tǒng)對(duì)內(nèi)存的管理可采取多種不同的方式,其中虛擬內(nèi)存的管理方式可用來(lái)管理大型的對(duì)象和結(jié)構(gòu)數(shù)組。

      在Windows系統(tǒng)中,任何一個(gè)進(jìn)程都被賦予其自己的虛擬地址空間,該虛擬地址空間覆蓋了一個(gè)相當(dāng)大的范圍,對(duì)于32位進(jìn)程,其地址空間為232=4,294,967,296 Byte,這使得一個(gè)指針可以使用從0x000000000xFFFFFFFF4GB范圍之內(nèi)的任何一個(gè)值。雖然每一個(gè)32位進(jìn)程可使用4GB的地址空間,但并不意味著每一個(gè)進(jìn)程實(shí)際擁有4GB的物理地址空間,該地址空間僅僅是一個(gè)虛擬地址空間,此虛擬地址空間只是內(nèi)存地址的一個(gè)范圍。進(jìn)程實(shí)際可以得到的物理內(nèi)存要遠(yuǎn)小于其虛擬地址空間。進(jìn)程的虛擬地址空間是為每個(gè)進(jìn)程所私有的,在進(jìn)程內(nèi)運(yùn)行的線程對(duì)內(nèi)存空間的訪問(wèn)都被限制在調(diào)用進(jìn)程之內(nèi),而不能訪問(wèn)屬于其他進(jìn)程的內(nèi)存空間。這樣,在不同的進(jìn)程中可以使用相同地址的指針來(lái)指向?qū)儆诟髯哉{(diào)用進(jìn)程的內(nèi)容而不會(huì)由此引起混亂。下面分別對(duì)虛擬內(nèi)存的各具體技術(shù)進(jìn)行介紹。
    地址空間中區(qū)域的保留與釋放

    在進(jìn)程創(chuàng)建之初并被賦予地址空間時(shí),其虛擬地址空間尚未分配,處于空閑狀態(tài)。這時(shí)地址空間內(nèi)的內(nèi)存是不能使用的,必須首先通過(guò)VirtualAlloc()函數(shù)來(lái)分配其內(nèi)的各個(gè)區(qū)域,對(duì)其進(jìn)行保留。

    LPVOID VirtualAlloc(
     LPVOID lpAddress,
     DWORD dwSize,
     DWORD flAllocationType,
     DWORD flProtect
    );

    其參數(shù)lpAddress包含一個(gè)內(nèi)存地址,用于定義待分配區(qū)域的首地址。通??蓪⒋藚?shù)設(shè)置為NULL,由系統(tǒng)通過(guò)搜索地址空間來(lái)決定滿足條件的未保留地址空間。這時(shí)系統(tǒng)可從地址空間的任意位置處開(kāi)始保留一個(gè)區(qū)域,而且還可以通過(guò)向參數(shù)flAllocationType設(shè)置MEM_TOP_DOWN標(biāo)志來(lái)指明在盡可能高的地址上分配內(nèi)存。如果不希望由系統(tǒng)自動(dòng)完成對(duì)內(nèi)存區(qū)域的分配而為lpAddress設(shè)定了內(nèi)存地址(必須確保其始終位于進(jìn)程的用戶模式分區(qū)中,否則將會(huì)導(dǎo)致分配的失敗),那么系統(tǒng)將在進(jìn)行分配之前首先檢查在該內(nèi)存地址上是否存在足夠大的未保留空間,如果存在一個(gè)足夠大的空閑區(qū)域,那么系統(tǒng)將會(huì)保留此區(qū)域并返回此保留區(qū)域的虛擬地址,否則將導(dǎo)致分配的失敗而返回NULL。這里需要特別指出的是,在指定lpAddress的內(nèi)存地址時(shí),必須確保是從一個(gè)分配粒度的邊界處開(kāi)始。
    一般來(lái)說(shuō),在不同的CPU平臺(tái)下分配粒度各不相同,但目前所有Windows環(huán)境下的CPUx8632Alpha、64Alpha以及IA-64等均是采用64KB的分配粒度。如果保留區(qū)域的起始地址沒(méi)有遵循從64KB分配粒度的邊界開(kāi)始之一原則,系統(tǒng)將自動(dòng)調(diào)整該地址到最接近的64K的倍數(shù)。例如,如果指定的lpAddress0x00781022,那么此保留區(qū)域?qū)嶋H是從0x00780000開(kāi)始分配的。參數(shù)dwSize指定了保留區(qū)域的大小。但是系統(tǒng)實(shí)際保留的區(qū)域大小必須是CPU頁(yè)面大小的整數(shù),如果指定的dwSize并非CPU頁(yè)面的整數(shù)系統(tǒng)將自動(dòng)對(duì)其進(jìn)行調(diào)整,使其達(dá)到與之最接近的頁(yè)面大小整數(shù)。與分配粒度一樣,對(duì)于不同的CPU平臺(tái)其頁(yè)面大小也是不一樣的。在x86平臺(tái)下,頁(yè)面大小為4KB,在32Alpah平臺(tái)下,頁(yè)面大小為8KB。在使用時(shí)可以通過(guò)GetSystemInfo()來(lái)決定當(dāng)前主機(jī)的頁(yè)面大小。參數(shù)flAllocationTypeflProtect分別定義了分配類(lèi)型和訪問(wèn)保護(hù)屬性。由于VirtualAlloc()可用來(lái)保留一個(gè)區(qū)域也可以用來(lái)占用物理存儲(chǔ)器,因此通過(guò)flAllocationType來(lái)指定當(dāng)前是要保留一個(gè)區(qū)域還是要占用物理存儲(chǔ)器。其可能使用的內(nèi)存分配類(lèi)型有:

    分配類(lèi)型

    類(lèi)型說(shuō)明

    MEM_COMMIT

    為特定的頁(yè)面區(qū)域分配內(nèi)存中或磁盤(pán)的頁(yè)面文件中的物理存儲(chǔ)

    MEM_PHYSICAL

    分配物理內(nèi)存(僅用于地址窗口擴(kuò)展內(nèi)存)

    MEM_RESERVE

    保留進(jìn)程的虛擬地址空間,而不分配任何物理存儲(chǔ)。保留頁(yè)面可通過(guò)繼續(xù)調(diào)用VirtualAlloc()而被占用

    MEM_RESET

    指明在內(nèi)存中由參數(shù)lpAddressdwSize指定的數(shù)據(jù)無(wú)效

    MEM_TOP_DOWN

    在盡可能高的地址上分配內(nèi)存(Windows 98忽略此標(biāo)志)

    MEM_WRITE_WATCH

    必須與MEM_RESERVE一起指定,使系統(tǒng)跟蹤那些被寫(xiě)入分配區(qū)域的頁(yè)面(僅針對(duì)Windows 98


      分配成功完成后,即在進(jìn)程的虛擬地址空間中保留了一個(gè)區(qū)域,可以對(duì)此區(qū)域中的內(nèi)存進(jìn)行保護(hù)權(quán)限許可范圍內(nèi)的訪問(wèn)。當(dāng)不再需要訪問(wèn)此地址空間區(qū)域時(shí),應(yīng)釋放此區(qū)域。由VirtualFree()負(fù)責(zé)完成。其函數(shù)原型為:

    BOOL VirtualFree(
     LPVOID lpAddress,
     DWORD dwSize,
     DWORD dwFreeType
    );

    其中,參數(shù)lpAddress為指向待釋放頁(yè)面區(qū)域的指針。如果參數(shù)dwFreeType指定了MEM_RELEASE,則lpAddress必須為頁(yè)面區(qū)域被保留時(shí)由VirtualAlloc()所返回的基地址。參數(shù)dwSize指定了要釋放的地址空間區(qū)域的大小,如果參數(shù)dwFreeType指定了MEM_RELEASE標(biāo)志,則將dwSize設(shè)置為0,由系統(tǒng)計(jì)算在特定內(nèi)存地址上的待釋放區(qū)域的大小。參數(shù)dwFreeType為所執(zhí)行的釋放操作的類(lèi)型,其可能的取值為MEM_RELEASEMEM_DECOMMIT,其中MEM_RELEASE標(biāo)志指明要釋放指定的保留頁(yè)面區(qū)域,MEM_DECOMMIT標(biāo)志則對(duì)指定的占用頁(yè)面區(qū)域進(jìn)行占用的解除。如果VirtualFree()成功執(zhí)行完成,將回收全部范圍的已分配頁(yè)面,此后如再對(duì)這些已釋放頁(yè)面區(qū)域內(nèi)存的訪問(wèn)將引發(fā)內(nèi)存訪問(wèn)異常。釋放后的頁(yè)面區(qū)域可供系統(tǒng)繼續(xù)分配使用。

      下面這段代碼演示了由系統(tǒng)在進(jìn)程的用戶模式分區(qū)內(nèi)保留一個(gè)64KB大小的區(qū)域,并將其釋放的過(guò)程:

    // 在地址空間中保留一個(gè)區(qū)域

    LPBYTE bBuffer = (LPBYTE)VirtualAlloc(NULL, 65536, MEM_RESERVE, PAGE_READWRITE);

    ……

    // 釋放已保留的區(qū)域

    VirtualFree(bBuffer, 0, MEM_RELEASE);

    flProtect頁(yè)面保護(hù)屬性

    我們可以給每個(gè)已分配的物理存儲(chǔ)頁(yè)指定不同的頁(yè)面保護(hù)屬性。表13-3列出了所有的頁(yè)面保護(hù)屬性。

    13-3  內(nèi)存頁(yè)面保護(hù)屬性

    保護(hù)屬性

     

    PAGE_NOACCESS

    試圖讀取頁(yè)面、寫(xiě)入頁(yè)面或執(zhí)行頁(yè)面中的代碼將引發(fā)訪問(wèn)違規(guī)

    PAGE_READONLY

    試圖寫(xiě)入頁(yè)面或執(zhí)行頁(yè)面中的代碼將引發(fā)訪問(wèn)違規(guī)

    PAGE_READWRITE

    試圖執(zhí)行頁(yè)面中的代碼將引發(fā)訪問(wèn)違規(guī)

    PAGE_EXECUTE

    試圖讀取頁(yè)面或?qū)懭腠?yè)面將引發(fā)訪問(wèn)違規(guī)

    PAGE_EXECUTE_READ

    試圖寫(xiě)入頁(yè)面將引發(fā)訪問(wèn)違規(guī)

    PAGE_EXECUTE_READWRITE

    對(duì)頁(yè)面執(zhí)行任何操作都不會(huì)引發(fā)訪問(wèn)違規(guī)

    PAGE_WRITECOPY

    試圖執(zhí)行頁(yè)面中的代碼將引發(fā)訪問(wèn)違規(guī)。試圖寫(xiě)入頁(yè)面將使系統(tǒng)為進(jìn)程單獨(dú)創(chuàng)建一份該頁(yè)面的私有副本(以頁(yè)交換文件為后備存儲(chǔ)器)

    PAGE_EXECUTE_WRITECOPY

    對(duì)頁(yè)面執(zhí)行任何操作都不會(huì)引發(fā)訪問(wèn)違規(guī)。試圖寫(xiě)入頁(yè)面將使系統(tǒng)為進(jìn)程單獨(dú)創(chuàng)建一份該頁(yè)面的私有副本(以頁(yè)交換文件為后備存儲(chǔ)器)

    一些惡意軟件將代碼寫(xiě)入到用于數(shù)據(jù)的內(nèi)存區(qū)域(比如線程),通過(guò)這種方式讓?xiě)?yīng)用程序執(zhí)行惡意代碼。Windows數(shù)據(jù)執(zhí)行保護(hù)(Data Execution Protection,后面簡(jiǎn)稱(chēng)為DEP)特性提供了對(duì)此類(lèi)惡意攻擊的防護(hù)。如果啟用了DEP,那么只有對(duì)那些真正需要執(zhí)行代碼的內(nèi)存區(qū)域,操作系統(tǒng)才會(huì)使用PAGE_EXECUTE_*保護(hù)屬性。其他保護(hù)屬性(最常見(jiàn)的就是PAGE_READWRITE)用于只應(yīng)該存放數(shù)據(jù)的內(nèi)存區(qū)域(比如線程和應(yīng)用程序的堆)。

    如果CPU試圖執(zhí)行某個(gè)頁(yè)面中的代碼,而該頁(yè)又沒(méi)有PAGE_EXECUTE_*保護(hù)屬性,那么CPU會(huì)拋出訪問(wèn)違規(guī)異常。

    系統(tǒng)還對(duì)Windows支持的結(jié)構(gòu)化異常處理機(jī)制(structured exception handling mechanism)做了更進(jìn)一步的保護(hù),結(jié)構(gòu)化異常處理機(jī)制會(huì)在第2325章詳細(xì)介紹。如果應(yīng)用程序在鏈接時(shí)使用了/SAFESEH開(kāi)關(guān),那么異常處理器會(huì)被注冊(cè)到映像文件中一個(gè)特殊的表中。這樣,當(dāng)將要執(zhí)行一個(gè)異常處理器時(shí),操作系統(tǒng)會(huì)先檢查該處理器有沒(méi)有在表中注冊(cè)過(guò),然后決定是否允許它執(zhí)行。

    有關(guān)DEP的更多信息,請(qǐng)?jiān)L問(wèn)http://go.microsoft.com/fwlink/?LinkId=28022,可以在此找到Microsoft白皮書(shū)“03_CIF_Memory_Protection.DOC”。

                                                                           

    13.6.1  寫(xiě)時(shí)復(fù)制

    在表13.3中列出的保護(hù)屬性中,除最后兩個(gè)屬性PAGE_WRITECOPYPAGE_EXECUTE_WRITECOPY之外,其余的都不言自明。這兩個(gè)保護(hù)屬性存在的目的是為了節(jié)省內(nèi)存和頁(yè)交換文件的使用。Windows支持一種機(jī)制,允許兩個(gè)或兩個(gè)以上的進(jìn)程共享同一塊存儲(chǔ)器。因此,如果有10個(gè)記事本程序正在運(yùn)行,所有的進(jìn)程會(huì)共享應(yīng)用程序的代碼頁(yè)和數(shù)據(jù)頁(yè)。讓所有的應(yīng)用程序?qū)嵗蚕硐嗤?span id="qumki20" class="GramE">存儲(chǔ)頁(yè)極大地提升了系統(tǒng)的性能,但另一方面,這也要求所有的應(yīng)用程序?qū)嵗荒茏x取其中的數(shù)據(jù)或是執(zhí)行其中的代碼。如果某個(gè)應(yīng)用程序?qū)嵗薷牟?xiě)入一個(gè)存儲(chǔ)頁(yè),那么這等于是修改了其他實(shí)例正在使用的存儲(chǔ)頁(yè),最終將導(dǎo)致混亂。

    為了避免此類(lèi)混亂的發(fā)生,操作系統(tǒng)會(huì)給共享的存儲(chǔ)頁(yè)指定寫(xiě)時(shí)復(fù)制屬性。當(dāng)系統(tǒng)把一個(gè).exe.dll映射到一個(gè)地址空間的時(shí)候,系統(tǒng)會(huì)計(jì)算有多少頁(yè)面是可寫(xiě)的。(通常,包含代碼的頁(yè)面被標(biāo)記為PAGE_EXECUTE_READ,而包含數(shù)據(jù)的頁(yè)面被標(biāo)記為PAGE_READWRITE。)然后系統(tǒng)會(huì)從頁(yè)交換文件中分配存儲(chǔ)空間來(lái)容納這些可寫(xiě)頁(yè)面。除非應(yīng)用程序真的寫(xiě)入可寫(xiě)頁(yè)面,否則不會(huì)用到頁(yè)交換文件中的存儲(chǔ)器。

    當(dāng)線程試圖寫(xiě)入一個(gè)共享頁(yè)面時(shí),系統(tǒng)會(huì)介入并執(zhí)行下面的操作。

    (1)   系統(tǒng)在內(nèi)存中找到一個(gè)閑置頁(yè)面。注意,該閑置頁(yè)面的后備頁(yè)面來(lái)自頁(yè)交換文件,它是系統(tǒng)最初將模塊映射到進(jìn)程的地址空間時(shí)分配的。由于系統(tǒng)在第一次進(jìn)行映射的時(shí)候分配了所有可能需要的頁(yè)交換文件空間,這一步不可能失敗。

    (2)   系統(tǒng)把線程想要修改的頁(yè)面內(nèi)容復(fù)制到在第1步中找到的閑置頁(yè)面。系統(tǒng)會(huì)給該閑置頁(yè)面指定PAGE_READWRITEPAGE_EXECUTE_READWRITE保護(hù)屬性,系統(tǒng)不會(huì)對(duì)原始頁(yè)面的保護(hù)屬性和數(shù)據(jù)做任何修改。

    (3)   然后,系統(tǒng)更新進(jìn)程的頁(yè)面表,這樣一來(lái),原來(lái)的虛擬地址現(xiàn)在就對(duì)應(yīng)到內(nèi)存中一個(gè)新的頁(yè)面了。

    系統(tǒng)在執(zhí)行這些步驟之后,進(jìn)程就可以訪問(wèn)它自己的副本了。第17章將進(jìn)一步介紹存儲(chǔ)器共享和寫(xiě)時(shí)復(fù)制。

    此外,在預(yù)訂地址空間或調(diào)撥物理存儲(chǔ)器時(shí),不能使用PAGE_WRITECOPYPAGE_EXECUTE_WRITECOPY保護(hù)屬性。這樣做會(huì)導(dǎo)致調(diào)用VirtualAlloc失敗,此時(shí)調(diào)用GetLastError會(huì)返回錯(cuò)誤碼ERROR_INVALID_PARAMETER。這兩個(gè)屬性是操作系統(tǒng)在映射.exeDLL映像文件時(shí)用的。

    13.6.2  一些特殊的訪問(wèn)保護(hù)屬性標(biāo)志

    除了已經(jīng)介紹過(guò)的保護(hù)屬性之外,另外還有3個(gè)保護(hù)屬性標(biāo)志:PAGE_NOCACHEPAGE_WRITECOMBINEPAGE_GUARD。使用這些標(biāo)志時(shí),只需將它們與除了PAGE_NOACCESS之外的任何其他保護(hù)屬性進(jìn)行按位或操作即可。

    第一個(gè)保護(hù)屬性標(biāo)志PAGE_NOCACHE,用來(lái)禁止對(duì)已調(diào)撥的頁(yè)面進(jìn)行緩存。該標(biāo)志存在的主要目的是為了讓需要操控內(nèi)存緩沖區(qū)的驅(qū)動(dòng)程序開(kāi)發(fā)人員使用,不建議將該標(biāo)志用于除此以外的其他用途。

                                                                           

    第二個(gè)保護(hù)屬性標(biāo)志PAGE_WRITECOMBINE也是給驅(qū)動(dòng)程序開(kāi)發(fā)人員用的。它允許把對(duì)單個(gè)設(shè)備的多次寫(xiě)操作組合在一起,以提高性能。

    最后一個(gè)保護(hù)屬性標(biāo)志PAGE_GUARD,使應(yīng)用程序能夠在頁(yè)面中的任何一個(gè)字節(jié)被寫(xiě)入時(shí)得到通知。這個(gè)標(biāo)志有一些巧妙的用法。Windows在創(chuàng)建線程時(shí)會(huì)用到它。有關(guān)該標(biāo)志的更多信息,請(qǐng)參閱第16章。


    物理存儲(chǔ)器的提交與回收

      在地址空間中保留一個(gè)區(qū)域后,并不能直接對(duì)其進(jìn)行使用,必須在把物理存儲(chǔ)器提交給該區(qū)域后,才可以訪問(wèn)區(qū)域中的內(nèi)存地址。在提交過(guò)程中,物理存儲(chǔ)器是按頁(yè)面邊界和頁(yè)面大小的塊來(lái)進(jìn)行提交的。若要為一個(gè)已保留的地址空間區(qū)域提交物理存儲(chǔ)器,需要再次調(diào)用VirtualAlloc()函數(shù),所不同的是在執(zhí)行物理存儲(chǔ)器的提交過(guò)程中需要指定flAllocationType參數(shù)為MEM_COMMIT標(biāo)志,使用的保護(hù)屬性與保留區(qū)域時(shí)所用保護(hù)屬性一致。在提交時(shí),可以將物理存儲(chǔ)器提交給整個(gè)保留區(qū)域,也可以進(jìn)行部分提交,由VirtualAlloc()函數(shù)的lpAddress參數(shù)和dwSize參數(shù)指明要將物理存儲(chǔ)器提交到何處以及要提交多少物理存儲(chǔ)器。
    與保留區(qū)域的釋放類(lèi)似,當(dāng)不再需要訪問(wèn)保留區(qū)域中被提交的物理存儲(chǔ)器時(shí),提交的物理存儲(chǔ)器應(yīng)得到及時(shí)的釋放。該回收過(guò)程與保留區(qū)域的釋放一樣也是通過(guò)VirtualFree()函數(shù)來(lái)完成的。在調(diào)用時(shí)為VirtualFree()的dwFreeType參數(shù)指定MEM_DECOMMIT標(biāo)志,并在參數(shù)lpAddressdwSize中傳遞用來(lái)標(biāo)識(shí)要解除的第一個(gè)頁(yè)面的內(nèi)存地址和釋放的字節(jié)數(shù)。此回收過(guò)程同樣也是以頁(yè)面為單位來(lái)進(jìn)行的,將回收設(shè)定范圍所涉及到的所有頁(yè)面。下面這段代碼演示了對(duì)先前保留區(qū)域的提交過(guò)程,并在使用完畢后將其回收:

    //
    在地址空間中保留一個(gè)區(qū)域

    LPBYTE bBuffer = (LPBYTE)VirtualAlloc(NULL, 65536, MEM_RESERVE, PAGE_READWRITE);

    // 提交物理存儲(chǔ)器

    VirtualAlloc(bBuffer, 65536, MEM_COMMIT, PAGE_READWRITE);

    ……

    // 回收提交的物理存儲(chǔ)器

    VirtualFree(bBuffer, 65536, MEM_DECOMMIT);

    // 釋放已保留的區(qū)域

    VirtualFree(bBuffer, 0, MEM_RELEASE);

     

      由于未經(jīng)提交的保留區(qū)域?qū)嶋H是無(wú)法使用的,因此在編程過(guò)程中允許通過(guò)一次VirtualAlloc()調(diào)用而完成對(duì)地址空間的區(qū)域保留及對(duì)保留區(qū)域的物理存儲(chǔ)器的提交。相應(yīng)的,回收、釋放過(guò)程也可由一次VirtualFree()調(diào)用來(lái)實(shí)現(xiàn)。上述代碼可按此方法改寫(xiě)為:

    // 在地址空間中保留一個(gè)區(qū)域并提交物理存儲(chǔ)器

    LPBYTE bBuffer = (LPBYTE)VirtualAlloc(NULL, 65536, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

    ……

    // 釋放已保留的區(qū)域并回收提交的物理存儲(chǔ)器

    VirtualFree(bBuffer, 0, MEM_RELEASE | MEM_DECOMMIT); 

    頁(yè)文件的使用

      在前面曾多次提到物理存儲(chǔ)器,這里所說(shuō)的物理存儲(chǔ)器并不局限于計(jì)算機(jī)內(nèi)存,還包括在磁盤(pán)空間上創(chuàng)建的頁(yè)文件,其存儲(chǔ)空間大小為計(jì)算機(jī)內(nèi)存和頁(yè)文件存儲(chǔ)容量之。由于通常情況下磁盤(pán)存儲(chǔ)空間要遠(yuǎn)大于內(nèi)存的存儲(chǔ)空間,因此頁(yè)文件的使用對(duì)于應(yīng)用程序而言相當(dāng)于透明的增加了其所能使用的內(nèi)存容量。在使用時(shí),由操作系統(tǒng)和CPU負(fù)責(zé)對(duì)頁(yè)文件進(jìn)行維護(hù)和協(xié)調(diào)。只有在應(yīng)用程序需要時(shí)才臨時(shí)將頁(yè)文件中的數(shù)據(jù)加載到內(nèi)存供應(yīng)用程序訪問(wèn)之用,在使用完畢后再?gòu)膬?nèi)存交換回頁(yè)文件

    進(jìn)程中的線程在訪問(wèn)位于已提交物理存儲(chǔ)器的保留區(qū)域的內(nèi)存地址時(shí),如果此地址指向的數(shù)據(jù)當(dāng)前已存在于內(nèi)存,CPU將直接將進(jìn)程的虛擬地址映射為物理地址,并完成對(duì)數(shù)據(jù)的訪問(wèn);如果此數(shù)據(jù)是存在于頁(yè)文件中的,就要試圖將此數(shù)據(jù)從頁(yè)文件加載到內(nèi)存。在進(jìn)行此處理時(shí),首先要檢查內(nèi)存中是否有可供使用的空閑頁(yè)面,如果有就可以直接將數(shù)據(jù)加載到內(nèi)存中的空閑頁(yè)面,否則就要從內(nèi)存中尋找一個(gè)暫不使用的可釋放的頁(yè)面并將數(shù)據(jù)加載到此頁(yè)面。如果被釋放頁(yè)面中的數(shù)據(jù)仍為有效數(shù)據(jù)(即以后還會(huì)用到),就要先將此頁(yè)面從內(nèi)存寫(xiě)入到頁(yè)文件。在數(shù)據(jù)加載到內(nèi)存后,仍要在CPU將虛擬地址映射為物理地址后方可實(shí)現(xiàn)對(duì)數(shù)據(jù)的訪問(wèn)。與對(duì)物理存儲(chǔ)器中數(shù)據(jù)的訪問(wèn)有所不同,在運(yùn)行可執(zhí)行程序時(shí)并不進(jìn)行程序代碼和數(shù)據(jù)的從磁盤(pán)文件到頁(yè)文件的復(fù)制過(guò)程,而是在確定了程序的代碼及其數(shù)據(jù)的大小后,由系統(tǒng)直接將可執(zhí)行程序的映像用作程序的保留地址空間區(qū)域。這樣的處理方式大大縮短了程序的啟動(dòng)時(shí)間,并可減小頁(yè)文件的尺寸。

     

     

    上面提到的“數(shù)據(jù)是否在內(nèi)存中”,我認(rèn)為應(yīng)該是判斷系統(tǒng)緩存中是否有需要的頁(yè)面。

    ==========================================================================================

    對(duì)內(nèi)存的管理

      使用虛擬內(nèi)存技術(shù)將能夠?qū)?nèi)存進(jìn)行管理。對(duì)當(dāng)前內(nèi)存狀態(tài)的動(dòng)態(tài)信息可通過(guò)GlobalMemoryStatus()函數(shù)來(lái)獲取。GlobalMemoryStatus()的函數(shù)原型為:

     

    VOID GlobalMemoryStatus(LPMEMORYSTATUS lpBuffer);

     

      其參數(shù)lpBuffer為一個(gè)指向內(nèi)存狀態(tài)結(jié)構(gòu)MEMORYSTATUS的指針,而且要預(yù)先對(duì)該結(jié)構(gòu)對(duì)象的數(shù)據(jù)成員進(jìn)行初始化。MEMORYSTATUS結(jié)構(gòu)定義如下:

     

    typedef struct _MEMORYSTATUS {

     DWORD dwLength; // MEMORYSTATUS結(jié)構(gòu)大小

     DWORD dwMemoryLoad; // 已使用內(nèi)存所占的百分比

     DWORD dwTotalPhys; // 物理存儲(chǔ)器的總字節(jié)數(shù)

     DWORD dwAvailPhys; // 空閑物理存儲(chǔ)器的字節(jié)數(shù)

     DWORD dwTotalPageFile; // 頁(yè)文件包含的最大字節(jié)數(shù)

     DWORD dwAvailPageFile; // 頁(yè)文件可用字節(jié)數(shù)

     DWORD dwTotalVirtual; // 用戶模式分區(qū)大小

     DWORD dwAvailVirtual; // 用戶模式分區(qū)中空閑內(nèi)存大小

    } MEMORYSTATUS, *LPMEMORYSTATUS;

    下面這段代碼通過(guò)設(shè)置一個(gè)定時(shí)器而每隔5秒更新一次當(dāng)前系統(tǒng)對(duì)內(nèi)存的使用情況:

    // 設(shè)置定時(shí)器

    SetTimer(0, 5000, NULL);

    ……

    void CSample22Dlg::OnTimer(UINT nIDEvent)

    {

     // 獲取當(dāng)前內(nèi)存使用狀態(tài)

     MEMORYSTATUS mst;

     GlobalMemoryStatus(&mst);

     // 已使用內(nèi)存所占的百分比

     m_dwMemoryLoad = mst.dwMemoryLoad;

     // 物理存儲(chǔ)器的總字節(jié)數(shù)

     m_dwAvailPhys = mst.dwAvailPhys / 1024;

     // 空閑物理存儲(chǔ)器的字節(jié)數(shù)

     m_dwAvailPageFile = mst.dwAvailPageFile / 1024;

     // 頁(yè)文件包含的最大字節(jié)數(shù)

     m_dwAvailVirtual = mst.dwAvailVirtual / 1024;

     // 頁(yè)文件可用字節(jié)數(shù)

     m_dwTotalPageFile = mst.dwTotalPageFile / 1024;

     // 用戶模式分區(qū)大小

     m_dwTotalPhys = mst.dwTotalPhys / 1024;

     // 用戶模式分區(qū)中空閑內(nèi)存大小

     m_dwTotalVirtual = mst.dwTotalVirtual / 1024;

     // 更新顯示

     UpdateData(FALSE);

     CDialog::OnTimer(nIDEvent);

    }

     

      對(duì)內(nèi)存的管理除了對(duì)當(dāng)前內(nèi)存的使用狀態(tài)信息進(jìn)行獲取外,還經(jīng)常需要獲取有關(guān)進(jìn)程的虛擬地址空間的狀態(tài)信息。可由VirtualQuery()函數(shù)來(lái)進(jìn)行查詢,其原型聲明如下:

     

    DWORD VirtualQuery(

     LPCVOID lpAddress, // 內(nèi)存地址

     PMEMORY_BASIC_INFORMATION lpBuffer, // 指向內(nèi)存信息結(jié)構(gòu)的指針

     DWORD dwLength // 內(nèi)存的大小

    );

     

      其中lpAddress參數(shù)為要查詢的虛擬內(nèi)存地址,該值將被調(diào)整到最近的頁(yè)邊界處。當(dāng)前計(jì)算機(jī)的頁(yè)面大小可通過(guò)GetSystemInfo()函數(shù)獲取,該函數(shù)需要一個(gè)指向SYSTEM_INFO結(jié)構(gòu)的指針作為參數(shù),獲取到的系統(tǒng)信息將填充在該數(shù)據(jù)結(jié)構(gòu)對(duì)象中。下面這段代碼通過(guò)對(duì)GetSystemInfo()的調(diào)用而獲取了當(dāng)前的系統(tǒng)信息:

     

    // 得到當(dāng)前系統(tǒng)信息

    GetSystemInfo(&m_sin);

    // 位屏蔽,指明哪個(gè)CPU是活動(dòng)的

    m_dwActiveProcessorMask = m_sin.dwActiveProcessorMask;

    // 保留的地址空間區(qū)域的分配粒度

    m_dwAllocationGranularity = m_sin.dwAllocationGranularity;

    // 進(jìn)程的可用地址空間的最小內(nèi)存地址

    m_dwMaxApplicationAddress = (DWORD)m_sin.lpMaximumApplicationAddress;

    // 進(jìn)程的可用地址空間的最大內(nèi)存地址

    m_dwMinApplicationAddress = (DWORD)m_sin.lpMinimumApplicationAddress;

    // 計(jì)算機(jī)中CPU的數(shù)目

    m_dwNumberOfProcessors = m_sin.dwNumberOfProcessors;

    // 頁(yè)面大小

    m_dwPageSize = m_sin.dwPageSize;

    // 處理器類(lèi)型

    m_dwProcessorType = m_sin.dwProcessorType;

    //進(jìn)一步細(xì)分處理器級(jí)別

    m_wProcessorLevel = m_sin.wProcessorLevel;

    // 系統(tǒng)處理器的結(jié)構(gòu)

    m_wProcessorArchitecture = m_sin.wProcessorArchitecture;

    // 更新顯示

    UpdateData(FALSE);

    VirtualQuery()的第二個(gè)參數(shù)lpBuffer為一個(gè)指向MEMORY_BASIC_INFORMATION結(jié)構(gòu)的指針。VirtualQuery()如成功執(zhí)行,該結(jié)構(gòu)對(duì)象中將保存查詢到的虛擬地址空間狀態(tài)信息。MEMORY_BASIC_INFORMATION結(jié)構(gòu)的定義為:

    typedef struct _MEMORY_BASIC_INFORMATION {

     PVOID BaseAddress; // 保留區(qū)域的基地址

     PVOID AllocationBase; // 分配的基地址

     DWORD AllocationProtect; // 初次保留時(shí)所設(shè)置的保護(hù)屬性

     DWORD RegionSize; // 區(qū)域大小

     DWORD State; // 狀態(tài)(提交、保留或空閑)

     DWORD Protect; // 當(dāng)前訪問(wèn)保護(hù)屬性

     DWORD Type; // 頁(yè)面類(lèi)型

    } MEMORY_BASIC_INFORMATION; 

     

      通過(guò)VirtualQuery()函數(shù)對(duì)由lpAddressdwLength參數(shù)指定的虛擬地址空間區(qū)域的查詢而獲取得到的相關(guān)狀態(tài)信息:

     

    // 更新顯示

    UpdateData(TRUE);

    // 虛擬地址空間狀態(tài)結(jié)構(gòu)

    MEMORY_BASIC_INFORMATION mbi;

    // 查詢指定虛擬地址空間的狀態(tài)信息

    VirtualQuery((LPCVOID)m_dwAddress, &mbi, 1024);

    // 保留區(qū)域的基地址

    m_dwBaseAddress = (DWORD)mbi.BaseAddress;

    // 分配的基地址

    m_dwAllocateBase = (DWORD)mbi.AllocationBase;

    // 初次保留時(shí)所設(shè)置的保護(hù)屬性

    m_dwAllocateProtect = mbi.AllocationProtect;

    // 區(qū)域大小

    m_dwRegionSize = mbi.RegionSize;

    // 狀態(tài)(提交、保留或空閑)

    m_dwState = mbi.State;

    // 當(dāng)前訪問(wèn)保護(hù)屬性

    m_dwProtect = mbi.Protect;

    // 頁(yè)面類(lèi)型

    m_dwType = mbi.Type;

    // 更新顯示

    UpdateData(FALSE);

     

      小結(jié)

     

      本文主要對(duì)內(nèi)存管理中的虛擬內(nèi)存技術(shù)的基本原理、使用方法和對(duì)內(nèi)存的管理等進(jìn)行了介紹。通過(guò)本文將能夠掌握虛擬內(nèi)存的一般使用方法,與之相關(guān)的內(nèi)存管理技術(shù)還包括內(nèi)存文件映射和堆管理等技術(shù),讀者可參閱相關(guān)文章。這幾種內(nèi)存管理技術(shù)同屬Windows編程中的高級(jí)技術(shù),在應(yīng)用程序中適當(dāng)使用將有助于程序性能的提高。本文所述程序在Windows 2000 Professional下由Microsoft Viusual C++ 6.0編譯通過(guò)。

    進(jìn)程的虛擬地址空間

    每個(gè)進(jìn)程都被賦予它自己的虛擬地址空間。對(duì)于3 2位進(jìn)程來(lái)說(shuō),這個(gè)地址空間是4 G B,因?yàn)?span lang="EN-US">3 2位指針可以擁有從0 x 0 0 0 0 0 0 0 00 x F F F F F F F F之間的任何一個(gè)值。這使得一個(gè)指針能夠擁有4 294 967 296個(gè)值中的一個(gè)值,它覆蓋了一個(gè)進(jìn)程的4 G B虛擬空間的范圍。對(duì)于6 4位進(jìn)程來(lái)說(shuō),這個(gè)地址空間是1 6 E B1 01 8字節(jié)),因?yàn)?span lang="EN-US">6 4位指針可以擁有從0 x 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 x F F F F F F F F F F F F F F F F之間的任何值。這使得一個(gè)指針可以擁有18 446 744 073 709 551 616個(gè)值中的一個(gè)值,它覆蓋了一個(gè)進(jìn)程的1 6 E B虛擬空間的范圍。這是相當(dāng)大的一個(gè)范圍。

    由于每個(gè)進(jìn)程可以接收它自己的私有的地址空間,因此當(dāng)進(jìn)程中的一個(gè)線程正在運(yùn)行時(shí),該線程可以訪問(wèn)只屬于它的進(jìn)程的內(nèi)存。屬于所有其他進(jìn)程的內(nèi)存則隱藏著,并且不能被正在運(yùn)行的線程訪問(wèn)。

    注意在Windows 2000中,屬于操作系統(tǒng)本身的內(nèi)存也是隱藏的,正在運(yùn)行的線程無(wú)法訪問(wèn)。這意味著線程常常不能訪問(wèn)操作系統(tǒng)的數(shù)據(jù)。Windows 98中,屬于操作系統(tǒng)的內(nèi)存是不隱藏的,正在運(yùn)行的線程可以訪問(wèn)。因此,正在運(yùn)行的線程常常可以訪問(wèn)操作系統(tǒng)的數(shù)據(jù),也可以破壞操作系統(tǒng)(從而有可能導(dǎo)致操作系統(tǒng)崩潰)。在Windows 98中,一個(gè)進(jìn)程的線程不可能訪問(wèn)屬于另一個(gè)進(jìn)程的內(nèi)存。

    前面說(shuō)過(guò),每個(gè)進(jìn)程有它自己的私有地址空間。進(jìn)程A可能有一個(gè)存放在它的地址空間中的數(shù)據(jù)結(jié)構(gòu),地址是0 x 1 2 3 4 5 6 7 8,而進(jìn)程B則有一個(gè)完全不同的數(shù)據(jù)結(jié)構(gòu)存放在它的地址空間中,地址是0 x 1 2 3 4 5 6 7 8。當(dāng)進(jìn)程A中運(yùn)行的線程訪問(wèn)地址為0 x 1 2 3 4 5 6 7 8的內(nèi)存時(shí),這些線程訪問(wèn)的是進(jìn)程A的數(shù)據(jù)結(jié)構(gòu)。當(dāng)進(jìn)程B中運(yùn)行的線程訪問(wèn)地址為0 x 1 2 3 4 5 6 7 8的內(nèi)存時(shí),這些線程訪問(wèn)的是進(jìn)程B的數(shù)據(jù)結(jié)構(gòu)。進(jìn)程A中運(yùn)行的線程不能訪問(wèn)進(jìn)程B的地址空間中的數(shù)據(jù)結(jié)構(gòu)。反之亦然。

    當(dāng)你因?yàn)閾碛腥绱舜蟮牡刂房臻g可以用于應(yīng)用程序而興高采烈之前,記住,這是個(gè)虛擬地址空間,不是物理地址空間。該地址空間只是內(nèi)存地址的一個(gè)范圍。在你能夠成功地訪問(wèn)數(shù)據(jù)而不會(huì)出現(xiàn)違規(guī)訪問(wèn)之前,必須賦予物理存儲(chǔ)器,或者將物理存儲(chǔ)器映射到各個(gè)部分的地址空間。本章后面將要具體介紹這是如何操作的。

    虛擬地址空間如何分區(qū)

    每個(gè)進(jìn)程的虛擬地址空間都要?jiǎng)澐殖筛鱾€(gè)分區(qū)。地址空間的分區(qū)是根據(jù)操作系統(tǒng)的基本實(shí)現(xiàn)方法來(lái)進(jìn)行的。不同的Wi n d o w s內(nèi)核,其分區(qū)也略有不同。表 1顯示了每種平臺(tái)是如何對(duì)進(jìn)程的地址空間進(jìn)行分區(qū)的。

    1 進(jìn)程的地址空間如何分區(qū)

    分區(qū)

    32Windows 2000(x86Alpha處理器)

    32Windows 2000(x86w/3GB用戶方式)

    64Windows 2000(AlphaIA-64處理器)

    Windows 98

    N U L L指針?lè)峙涞姆謪^(qū)

    0 x 0 0 0 0 0 0 0 0  ——0x 0 0 0 0 F F F F

    0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 F F F F

    0x00000000 00000000 0x00000000 0000FFFF

    0 x 0 0 0 0 0 0 0 0 0 x 0 0 0 0 0 F F F

    DOS/16Windows應(yīng)用程序兼容分區(qū)

    無(wú)

    無(wú)

    無(wú)

    0 x 0 0 0 0 0 1 0 0 0 0 x 0 0 3 F F F F F

    用戶方式

    0 x 0 0 0 1 0 0 0 0—— 0 x 7 F F E F F F F<將近2G>

    0 x 0 0 0 1 0 0 0 0 0 x B F F E F F F F F

    0x00000000 00010000 0x000003FF FFFEFFFF

    0 x 0 0 4 0 0 0 0 0 0 x 7 F F F F F F F

    64-KB禁止進(jìn)入分區(qū)

    0 x 7 F F F 0 0 0 0——0x7FFF FFFF

    0 x B F F F 0 0 0 0——0 x B F F F F F F F

    0 x 0 0 0 0 0 3 F F F F F F 0 0 0 0——0 x 0 0 0 0 0 3 F F F F F F F F F F

    無(wú)

    共享內(nèi)存映射

    無(wú)

    無(wú)

    無(wú)

    0 x 8 0 0 0 0 0 0 0

    文件(MMF)內(nèi)核方式

    0 x 8 0 0 0 0 0 0 0 —— 0 x F F F F F F F F<2G>

    0 x C 0 0 0 0 0 0 0 0 x F F F F F F F F

    0x00000400 00000000 0xFFFFFFFFF FFFFFFF

    0 x B F F F F F F F 0 x C 0 0 0 0 0 0 0 0 x F F F F F F F F

    1. NULL指針?lè)謪^(qū)是NULL指針的地址范圍。
        
    對(duì)這個(gè)區(qū)域的讀寫(xiě)企圖都將引發(fā)訪問(wèn)違規(guī)。 
    2. DOS/WIN16
    分區(qū)是98中專(zhuān)門(mén)用于16位的
        DOS
    windows程序運(yùn)行的空間,所有的16
        
    位程序?qū)⒐蚕磉@個(gè)4M的空間。Win2000中不
        
    存在這個(gè)分區(qū),16位程序也會(huì)擁有自己獨(dú)立的虛擬地址空間。有的文章中稱(chēng)win2000中不能運(yùn)行16位程序,是不確切的。 
    3.
    用戶分區(qū)是進(jìn)程的私有領(lǐng)域,Win2000中,程序的可執(zhí)行代碼和其它用戶模塊均加載在這里,內(nèi)存映射文件也會(huì)加載在這里。Win98中的系統(tǒng)共享DLL和內(nèi)存映射文件則加載在共享分區(qū)中。 
    4.
    禁止訪問(wèn)分區(qū)只有在win2000中有。這個(gè)分區(qū)是用戶分區(qū)和內(nèi)核分區(qū)之間的一個(gè)隔離帶,目的是為了防止用戶程序違規(guī)訪問(wèn)內(nèi)核分區(qū)。 
    5. MMF
    分區(qū)只有win98中有,所有的內(nèi)存映射文件和系統(tǒng)共享DLL將加載在這個(gè)地址。而2000中則將其加載到用戶分區(qū)。 
    6. 
    內(nèi)核方式分區(qū)對(duì)用戶的程序來(lái)說(shuō)是禁止訪問(wèn)的,操作系統(tǒng)的代碼在此。內(nèi)核對(duì)象也駐留在此。
    另外要說(shuō)明的是,win98中對(duì)于內(nèi)核分區(qū)本也應(yīng)該提供保護(hù)的,但遺憾的是并沒(méi)有做到,因而98中程序可以訪問(wèn)內(nèi)核分區(qū)的地址空間。
    對(duì)于用戶分區(qū),又可以細(xì)分成若干區(qū)域。(這些區(qū)域具體會(huì)在第四階段詳細(xì)剖析。因?yàn)檫@部分內(nèi)容牽扯到PE文件結(jié)構(gòu),只有學(xué)習(xí)并理解了PE文件結(jié)構(gòu)后,才能理解這部分內(nèi)容,為了便于后面的講解,在此講這部分區(qū)域先大致分為4塊:)

    3 2Windows 2000的內(nèi)核與6 4Windows 2000的內(nèi)核擁有大體相同的分區(qū),差別在于分區(qū)的大小和位置有所不同。另一方面,可以看到Windows 98下的分區(qū)有著很大的不同。下面讓我們看一下系統(tǒng)是如何使用每一個(gè)分區(qū)的。

    NULL指針?lè)峙涞姆謪^(qū)適用于Windows 2000Windows 98

    進(jìn)程地址空間的這個(gè)分區(qū)的設(shè)置是為了幫助程序員掌握N U L L指針的分配情況。如果你的進(jìn)程中的線程試圖讀取該分區(qū)的地址空間的數(shù)據(jù),或者將數(shù)據(jù)寫(xiě)入該分區(qū)的地址空間,那么C P U就會(huì)引發(fā)一個(gè)訪問(wèn)違規(guī)。保護(hù)這個(gè)分區(qū)是極其有用的,它可以幫助你發(fā)現(xiàn)N U L L指針的分配情況。

    C / C + +程序中常常不進(jìn)行嚴(yán)格的錯(cuò)誤檢查。例如,下面這個(gè)代碼就沒(méi)有進(jìn)行任何錯(cuò)誤檢查:

    int* pnSomeInteger = (int*) malloc(sizeof(int));
    *pnSomeInteger = 5;

    如果m a l l o c不能找到足夠的內(nèi)存來(lái)滿足需要,它就返回N U L L。但是,該代碼并不檢查這種可能性,它認(rèn)為地址的分配已經(jīng)取得成功,并且開(kāi)始訪問(wèn)0 x 0 0 0 0 0 0 0 0地址的內(nèi)存。由于這個(gè)分區(qū)的地址空間是禁止進(jìn)入的,因此就會(huì)發(fā)生內(nèi)存訪問(wèn)違規(guī)現(xiàn)象,同時(shí)該進(jìn)程將終止運(yùn)行。這個(gè)特性有助于編程員發(fā)現(xiàn)應(yīng)用程序中的錯(cuò)誤。

    用戶方式分區(qū)適用于Windows 2000Windows 98

    這個(gè)分區(qū)是進(jìn)程的私有(非共享)地址空間所在的地方。一個(gè)進(jìn)程不能讀取、寫(xiě)入、或者以任何方式訪問(wèn)駐留在該分區(qū)中的另一個(gè)進(jìn)程的數(shù)據(jù)。對(duì)于所有應(yīng)用程序來(lái)說(shuō),該分區(qū)是維護(hù)進(jìn)程的大部分?jǐn)?shù)據(jù)的地方。由于每個(gè)進(jìn)程可以得到它自己的私有的、非共享分區(qū),以便存放它的數(shù)據(jù),因此,應(yīng)用程序不太可能被其他應(yīng)用程序所破壞,這使得整個(gè)系統(tǒng)更加健壯。

    Windows 2000中,所有的. e x eD L L模塊均加載這個(gè)分區(qū)。每個(gè)進(jìn)程可以將這些D L L加載到該分區(qū)的不同地址中(不過(guò)這種可能性很小)。系統(tǒng)還可以在這個(gè)分區(qū)中映射該進(jìn)程可以訪問(wèn)的所有內(nèi)存映射文件

    共享的MMF分區(qū)僅適用于Windows 98

    這個(gè)1 G B分區(qū)是系統(tǒng)用來(lái)存放所有3 2位進(jìn)程共享數(shù)據(jù)的地方。例如,系統(tǒng)的動(dòng)態(tài)鏈接庫(kù)K e r n e l 3 2 . d l lA d v A P I 3 2 . d l l、U s e r 3 2 . d l lG D I 3 2 . d l l等,全部存放在這個(gè)地址空間分區(qū)中,因此,所有3 2位進(jìn)程都能很容易同時(shí)訪問(wèn)它們。系統(tǒng)還為每個(gè)進(jìn)程將D L L加載相同的內(nèi)存地址。此外,系統(tǒng)將所有內(nèi)存映射文件映射到這個(gè)分區(qū)中。

    物理存儲(chǔ)器與頁(yè)文件

    在較老的操作系統(tǒng)中,物理存儲(chǔ)器被視為計(jì)算機(jī)擁有的R A M的容量。換句話說(shuō),如果計(jì)算機(jī)擁有1 6 M BR A M,那么加載和運(yùn)行的應(yīng)用程序最多可以使用1 6 M BR A M。今天的操作系統(tǒng)能夠使得磁盤(pán)空間看上去就像內(nèi)存一樣。磁盤(pán)上的文件通常稱(chēng)為頁(yè)文件,它包含了可供所有進(jìn)程使用的虛擬內(nèi)存

    當(dāng)然,若要使虛擬內(nèi)存能夠運(yùn)行,需要得到C P U本身的大量幫助。當(dāng)一個(gè)線程試圖訪問(wèn)一個(gè)字節(jié)的內(nèi)存時(shí), C P U必須知道這個(gè)字節(jié)是在R A M中還是在磁盤(pán)上。

    從應(yīng)用程序的角度來(lái)看,頁(yè)文件透明地增加了應(yīng)用程序能夠使用的R A M(即內(nèi)存)的數(shù)量。如果計(jì)算機(jī)擁有6 4 M BR A M,同時(shí)在硬盤(pán)上有一個(gè)100 MB的頁(yè)文件,那么運(yùn)行的應(yīng)用程序就認(rèn)為計(jì)算機(jī)總共擁有1 6 4 M BR A M

    實(shí)際上并不擁有1 6 4 M BR A M。相反,操作系統(tǒng)與C P U相協(xié)調(diào),共同將R A M的各個(gè)部分保存到頁(yè)文件中,當(dāng)運(yùn)行的應(yīng)用程序需要時(shí),再將頁(yè)文件的各個(gè)部分重新加載到R A M。由于頁(yè)文件增加了應(yīng)用程序可以使用的R A M的容量,因此頁(yè)文件的使用是視情況而定的。如果沒(méi)有頁(yè)文件,那么系統(tǒng)就認(rèn)為只有較少的R A M可供應(yīng)用程序使用。但是,我們鼓勵(lì)用戶使用頁(yè)文件,這樣他們就能夠運(yùn)行更多的應(yīng)用程序,并且這些應(yīng)用程序能夠?qū)Ω蟮臄?shù)據(jù)集進(jìn)行操作。最好將物理存儲(chǔ)器視為存儲(chǔ)在磁盤(pán)驅(qū)動(dòng)器(通常是硬盤(pán)驅(qū)動(dòng)器)上的頁(yè)文件中的數(shù)據(jù)。這樣,當(dāng)一個(gè)應(yīng)用程序通過(guò)調(diào)用Vi r t u a l A l l o c函數(shù),將物理存儲(chǔ)器提交給地址空間的一個(gè)區(qū)域時(shí),地址空間實(shí)際上是從硬盤(pán)上的一個(gè)文件中進(jìn)行分配的。系統(tǒng)的頁(yè)文件的大小是確定有多少物理存儲(chǔ)器可供應(yīng)用程序使用時(shí)應(yīng)該考慮的最重要的因素, R A M的容量則影響非常小。

    第一種情況中,線程試圖訪問(wèn)的數(shù)據(jù)是在R A M中。在這種情況下, C P U將數(shù)據(jù)的虛擬內(nèi)存地址映射到內(nèi)存的物理地址中,然后執(zhí)行需要的訪問(wèn)。線程試圖訪問(wèn)的數(shù)據(jù)不在R A M中,而是存放在頁(yè)文件中的某個(gè)地方。這時(shí),試圖訪問(wèn)就稱(chēng)為頁(yè)面失效, C P U將把試圖進(jìn)行的訪問(wèn)通知操作系統(tǒng)。這時(shí)操作系統(tǒng)就尋找R A M中的一個(gè)內(nèi)存空頁(yè)。如果找不到空頁(yè),系統(tǒng)必須釋放一個(gè)空頁(yè)。如果一個(gè)頁(yè)面尚未被修改,系統(tǒng)就可以釋放該頁(yè)面。但是,如果系統(tǒng)需要釋放一個(gè)已經(jīng)修改的頁(yè)面,那么它必須首先將該頁(yè)面從R A M拷貝到頁(yè)交換文件中,然后系統(tǒng)進(jìn)入該頁(yè)文件,找出需要訪問(wèn)的數(shù)據(jù)塊,并將數(shù)據(jù)加載到空閑的內(nèi)存頁(yè)面。然后,操作系統(tǒng)更新它的用于指明數(shù)據(jù)的虛擬內(nèi)存地址現(xiàn)在已經(jīng)映射到R A M中的相應(yīng)的物理存儲(chǔ)器地址中的表。這時(shí)C P U重新運(yùn)行生成初始頁(yè)面失效的指令,但是這次C P U能夠?qū)⑻摂M內(nèi)存地址映射到一個(gè)物理R A M地址,并訪問(wèn)該數(shù)據(jù)塊。

    當(dāng)閱讀了上一節(jié)后,你必定會(huì)認(rèn)為,如果同時(shí)運(yùn)行許多文件的話,頁(yè)文件就可能變得非常大,而且你會(huì)認(rèn)為,每當(dāng)你運(yùn)行一個(gè)程序時(shí),系統(tǒng)必須為進(jìn)程的代碼和數(shù)據(jù)保留地址空間的一些區(qū)域,將物理存儲(chǔ)器提交給這些區(qū)域,然后將代碼和數(shù)據(jù)從硬盤(pán)上的程序文件拷貝到頁(yè)文件中已提交的物理存儲(chǔ)器中。

    實(shí)際上系統(tǒng)并不進(jìn)行上面所說(shuō)的這些操作。如果它進(jìn)行這些操作的話,就要花費(fèi)很長(zhǎng)的時(shí)間來(lái)加載程序并啟動(dòng)它運(yùn)行。相反,當(dāng)啟動(dòng)一個(gè)應(yīng)用程序的時(shí)候,系統(tǒng)將打開(kāi)該應(yīng)用程序的. e x e文件,確定該應(yīng)用程序的代碼和數(shù)據(jù)的大小。然后系統(tǒng)要保留一個(gè)地址空間的區(qū)域,并指明與該區(qū)域相關(guān)聯(lián)的物理存儲(chǔ)器是在. e x e文件本身中。即系統(tǒng)并不是從頁(yè)文件中分配地址空間,而是將. e x e文件的實(shí)際內(nèi)容即映像用作程序的保留地址空間區(qū)域。當(dāng)然,這使應(yīng)用程序的加載非常迅速,并使頁(yè)文件能夠保持得非常小

    一、開(kāi)始之前,讓我們來(lái)了解一下Windows中內(nèi)存管理的一些知識(shí):

     

    1. 機(jī)器的物理內(nèi)存由兩部分組成。一部分為機(jī)器的主存RAM,也就是我們內(nèi)存條的大小;另一部分為虛擬內(nèi)存,它就在機(jī)器的硬盤(pán)上,以頁(yè)文件的形式存在。

     

    2. 每個(gè)進(jìn)程都有自己的虛擬地址空間,對(duì)于具有32位尋址能力的機(jī)器來(lái)說(shuō),這個(gè)虛擬空間的大小為4GB?,F(xiàn)在我們使用的機(jī)器就是4GB

     

    3. 進(jìn)程的4GB虛擬地址空間又可以分成幾個(gè)部分,其中進(jìn)程真正私有的空間少于2GB(這段地址空間被稱(chēng)作“用戶方式分區(qū)”),其余的2GB多空間都是給操作系統(tǒng)的,且這部分空間被所有的進(jìn)程共享。(參考Windows核心編程Chapter 13

     

    4. 為進(jìn)程“分配內(nèi)存”,這個(gè)概念可以細(xì)化:“保留段地址空間”,“提交一段內(nèi)存空間”,“將內(nèi)存空間映射到主存”。在程序中我們通常所訪問(wèn)的地址都必須是進(jìn)程地址空間中被保留和提交的那段地址空間。

     

    4.1 “保留段地址空間”:即從進(jìn)程4GB地址空間中保留段地址空間,這個(gè)過(guò)程通過(guò)VirtualAlloc函數(shù)完成,并把分配類(lèi)型參數(shù)設(shè)置為MEM_RESERVE。這段空間的起始地址必須是系統(tǒng)分配粒度的整數(shù),大小必須是系統(tǒng)頁(yè)面大小的整數(shù)

     

    4.2 “提交一段內(nèi)存空間”:即為進(jìn)程已保留的地址空間映射機(jī)器的物理內(nèi)存,這里要特別注意,所謂物理內(nèi)存一般并不是機(jī)器的主存,而只是機(jī)器的虛擬內(nèi)存。這個(gè)過(guò)程同樣又VirtualAlloc完成,只是把分配類(lèi)型參數(shù)設(shè)置為MEM_COMMIT。這段空間的起始地址和大小都必須是頁(yè)面大小的整數(shù)。這樣進(jìn)程的對(duì)應(yīng)被提交的區(qū)域就被映射到機(jī)器的虛擬內(nèi)存上。

     

    4.3 “將內(nèi)存空間映射到主存”:這點(diǎn)很重要,操作系統(tǒng)總是只有在進(jìn)程提交的頁(yè)面被訪問(wèn)時(shí)才將相應(yīng)的頁(yè)面加載到主存中,同時(shí)修改進(jìn)程對(duì)應(yīng)頁(yè)面的地址空間映射。這時(shí),進(jìn)程的地址空間中的對(duì)應(yīng)區(qū)域才和機(jī)器上的主存對(duì)應(yīng)起來(lái)。

     

    Virtual Size

     

          該指標(biāo)記錄了當(dāng)前進(jìn)程申請(qǐng)成功的其虛擬地址空間的總的空間大小,包括DLL/EXE占用的地址和通過(guò)VirtualAlloc API ReserveMemory Space數(shù)量。請(qǐng)注意,該指標(biāo)包括保留的地址空間。

     

    Private Bytes

     

           該指標(biāo)記錄了進(jìn)程用戶方式分區(qū)地址空間中已提交的總的空間大小。無(wú)論是直接調(diào)用API申請(qǐng)的內(nèi)存,被Heap Manager申請(qǐng)的內(nèi)存,或者是CLR managed heap,都算在里面。

     

    Working Set

     

           該指標(biāo)記錄了所有映射到進(jìn)程虛擬地址空間的機(jī)器主存的大小,它不僅僅是用戶方式分區(qū)部分的映射,而是整個(gè)進(jìn)程地址空間的映射。即它同時(shí)包括內(nèi)核方式分區(qū)中映射到機(jī)器主存的部分。由4.3可知,在用戶方式分區(qū)部分只有在進(jìn)程提交的頁(yè)面被訪問(wèn)時(shí)才將相應(yīng)的頁(yè)面加載到主存中。而對(duì)于該部分的大小總是系統(tǒng)頁(yè)面大小的整數(shù)。

     

           這里有一個(gè)問(wèn)題,隨著進(jìn)程的不斷運(yùn)行,進(jìn)程被訪問(wèn)的頁(yè)面將可能不斷增加,這是否意味著“Working Set”的大小會(huì)不斷的累加呢?顯然不是。在程序運(yùn)行過(guò)程中影響“Working Set”的因素包括:(1) 機(jī)器可用主存的大小 (2) 進(jìn)程本身“Working Set”的大小范圍。當(dāng)機(jī)器的可用主存小于一定值時(shí),系統(tǒng)會(huì)釋放一些老的最近沒(méi)有被訪問(wèn)的頁(yè)面,把這些頁(yè)面通過(guò)交換文件交換到機(jī)器的虛擬內(nèi)存中;當(dāng)Working Set的大小大于該進(jìn)程所設(shè)置的最大值時(shí),同樣會(huì)把一些老的頁(yè)面交換到機(jī)器的虛擬內(nèi)存中。當(dāng)這些頁(yè)面下次再被訪問(wèn)時(shí),它們才加載到主存。

     

           由上可知,Working Set“一定比”Private Bytes“小,因?yàn)樗皇?#8221;Private Bytes“對(duì)應(yīng)的地址空間中被加載到主存的那部分

     

    Page Faults”

     

           該指標(biāo)和Working Set密切相關(guān),當(dāng)進(jìn)程訪問(wèn)某個(gè)頁(yè)面,而這個(gè)頁(yè)面卻不在主存中時(shí),就要發(fā)生一次Page Fault“,即進(jìn)程訪問(wèn)非”Working Set“中的頁(yè)面時(shí),發(fā)生一次”Page Fault“,同時(shí)系統(tǒng)將對(duì)應(yīng)頁(yè)面加載到主存中。

     

           接下來(lái)的三個(gè)指標(biāo)是對(duì)Working Set“的細(xì)化:

     

    WS Private“

     

           該指標(biāo)記錄了進(jìn)程Working Set“中被該進(jìn)程所獨(dú)享的空間大小。

     

    "WS Shareable"

     

           該指標(biāo)記錄了進(jìn)程Working Set“中能與別的進(jìn)程共享的空間大小

     

    WS Shared“

     

           該指標(biāo)記錄了進(jìn)程Working Set“中已經(jīng)與別的進(jìn)程共享的空間大小

     

    WS Shareable“和”WS Shared“兩個(gè)指標(biāo)一看令人感到疑惑,因?yàn)榧热?#8221;Working Set“屬于”Private Bytes“中的一部分,而”Private Bytes“是進(jìn)程私有的,為什么會(huì)有”WS Shareable“和”WS Shared“這兩項(xiàng)呢?

     

           認(rèn)真一想,其實(shí)很容易理解,比如兩個(gè)進(jìn)程都需要同一個(gè)DLL的支持,所以在進(jìn)程運(yùn)行過(guò)程中,這個(gè)DLL被映射到了兩個(gè)進(jìn)程的地址空間中,如果這個(gè)DLL的大小為4K,在兩個(gè)進(jìn)程中都要提交4K的虛擬地址空間來(lái)映射這個(gè)DLL。當(dāng)?shù)谝粋€(gè)進(jìn)程訪問(wèn)了這個(gè)DLL時(shí),這個(gè)DLL被加載到機(jī)器主存中,這時(shí),第二個(gè)進(jìn)程也要訪問(wèn)該DLL,這時(shí),系統(tǒng)就不會(huì)再加載一遍該DLL了,因?yàn)檫@個(gè)DLL已經(jīng)在主存中了。當(dāng)然上面所說(shuō)的訪問(wèn)僅僅是讀取的操作,如果這時(shí)候某個(gè)進(jìn)程要修改DLL對(duì)應(yīng)這段地址中的某個(gè)單元時(shí),這時(shí),系統(tǒng)必須為第二個(gè)進(jìn)程分配另外的新頁(yè)面,并把要修改位置對(duì)應(yīng)的頁(yè)面拷貝的這個(gè)新頁(yè)面,同時(shí),第二個(gè)進(jìn)程中的這個(gè)DLL被映射到這個(gè)新頁(yè)面上。

     

           上面的分析中,DLL對(duì)應(yīng)的4K的內(nèi)存在第一個(gè)進(jìn)程中便是WS Shareable“。另外,內(nèi)核方式分區(qū)中的所有代碼都是被所有進(jìn)程共享的,只要一個(gè)進(jìn)程訪問(wèn)了這些頁(yè)面,則在所有的進(jìn)程的Working Set“中都能體現(xiàn)。

     

    三、下面我們來(lái)討論一下這些內(nèi)存指標(biāo)與進(jìn)程內(nèi)存消耗之間的關(guān)系

     

           在計(jì)算機(jī)更新?lián)Q代不斷加速的今天,我們往往很少關(guān)注程序?qū)?nèi)存的消耗,除非程序的內(nèi)存消耗超出了我們的忍受范圍——大量的泄漏、運(yùn)行速度下降等。

     

           那么,當(dāng)我們?cè)?span id="ke0kyq0" class="GramE">測(cè)進(jìn)程的內(nèi)存使用量時(shí),到底應(yīng)該使用哪個(gè)指標(biāo)能更好的反應(yīng)程序的內(nèi)存消耗呢?由于Windows自帶的Task Manager中的Memory Usage“所對(duì)應(yīng)的指標(biāo)就是”Working Set,所以大部分人認(rèn)為該指標(biāo)能夠很好的反應(yīng)進(jìn)程的內(nèi)存使用量。

     

    在得出結(jié)論之前,讓我們來(lái)分析一下以上的這些指標(biāo):

     

    就從Working Set“開(kāi)始吧。

     

    Working Set“:

     

           進(jìn)程中被加載到機(jī)器主存的所有頁(yè)面大小的。它可細(xì)分為WS Shareable“和”WS Shared“。進(jìn)程訪問(wèn)頁(yè)面不再Working Set“中時(shí),會(huì)發(fā)生一次”Page Fault“且同時(shí)發(fā)生一次主存與虛擬內(nèi)存之間的數(shù)據(jù)交換。綜上所述,我們可以得出結(jié)論:

     

    (a)Working Set“不是進(jìn)程內(nèi)存消耗的全部;

     

    (b)所有進(jìn)程Working Set“的和也不等有機(jī)器主存總的消耗量,因?yàn)榇嬖?#8221;Working Shareable“與別的進(jìn)程共享;

     

    (c)Working Set“太大會(huì)影響機(jī)器的運(yùn)行速度,因?yàn)?#8221;Working Set“太大會(huì)導(dǎo)致機(jī)器的可用主存太少,從而導(dǎo)致將進(jìn)程的老頁(yè)面釋放到虛擬內(nèi)存,同時(shí),進(jìn)程”Working Set“中的頁(yè)面減少后,使進(jìn)程發(fā)生”Page Fault“的頻率更高。因?yàn)樵谥鞔媾c虛擬內(nèi)存之間交換數(shù)據(jù)需要時(shí)間,所以機(jī)器的運(yùn)行速度要減慢。

     

    (d)Working Set“由于數(shù)據(jù)交換的存在,該指標(biāo)是動(dòng)態(tài)的,在測(cè)量的過(guò)程中會(huì)不斷變化。(變化的最小單位為4K

     

           所以Working Set“指標(biāo)強(qiáng)調(diào)的是進(jìn)程對(duì)機(jī)器主存的消耗,不是進(jìn)程內(nèi)存的全部信息。

     

    "Private Bytes"

     

           該指標(biāo)包含所有為進(jìn)程提交的內(nèi)存,包括機(jī)器主存和虛擬內(nèi)存,可以認(rèn)為它是進(jìn)程對(duì)物理內(nèi)存消耗,且該指標(biāo)相對(duì)來(lái)說(shuō)更加穩(wěn)定。在程序產(chǎn)生內(nèi)存泄漏時(shí),該值一定是不斷上漲的。

     

           綜上所述,個(gè)人更傾向于使用Private Bytes“來(lái)定量進(jìn)程的內(nèi)存消耗和分析進(jìn)程的內(nèi)存泄漏。

    posted on 2008-11-20 11:15 so true 閱讀(23649) 評(píng)論(7)  編輯  收藏 所屬分類(lèi): Others

    評(píng)論

    # re: 進(jìn)程的虛擬地址空間  回復(fù)  更多評(píng)論   

    這篇文章是我本人在互聯(lián)網(wǎng)上找到的關(guān)于進(jìn)程地址空間說(shuō)的最好了了,歡迎來(lái)頂作者!這樣的文章真的太珍貴?。?!
    2011-03-31 16:13 | yiruirui

    # re: 進(jìn)程的虛擬地址空間  回復(fù)  更多評(píng)論   

    @yiruirui
    好文章,收藏了,好好學(xué)習(xí)一下.
    2011-11-01 08:45 | scient

    # re: 進(jìn)程的虛擬地址空間  回復(fù)  更多評(píng)論   

    非常好的文章?。。?
    2012-02-14 15:52 | shory

    # re: 進(jìn)程的虛擬地址空間  回復(fù)  更多評(píng)論   

    好文章,感謝樓主~
    2012-04-02 09:47 | adaByron

    # re: 進(jìn)程的虛擬地址空間  回復(fù)  更多評(píng)論   

    神,你是怎么煉成的
    2012-05-07 08:49 | readily

    # re: 進(jìn)程的虛擬地址空間[未登錄](méi)  回復(fù)  更多評(píng)論   

    exe通過(guò)內(nèi)存文件映射成為進(jìn)程虛擬地址空間。那么exe文件中的 導(dǎo)出表 導(dǎo)入表 等等section是否也映射到虛擬地址空間里面了呢?還是只映射了
    -------------
    程序代碼和數(shù)據(jù)

    其他庫(kù)文件

    內(nèi)核虛擬存儲(chǔ)器
    --------------------
    這些東西?
    文件映射都是進(jìn)程地址空間保留了文件中的所有內(nèi)容。
    那么進(jìn)程的地址空間中是否也保留了exe中的所有內(nèi)容?
    2013-04-10 20:19 | kenny

    # re: 進(jìn)程的虛擬地址空間[未登錄](méi)  回復(fù)  更多評(píng)論   

    寫(xiě)的真是太透徹了
    2013-10-28 22:30 | ccc
    主站蜘蛛池模板: 日韩精品内射视频免费观看| 亚洲网红精品大秀在线观看| 久久久精品视频免费观看| 亚洲成?Ⅴ人在线观看无码| 国产精品亚洲专区一区| 全部免费毛片在线| 搜日本一区二区三区免费高清视频 | 精品国产成人亚洲午夜福利| 91情侣在线精品国产免费| 亚洲国产日韩精品| 大香人蕉免费视频75| 苍井空亚洲精品AA片在线播放| 永久免费无码网站在线观看| 亚洲AV无码专区在线电影成人| 免费毛片在线播放| 牛牛在线精品免费视频观看| 精品亚洲成α人无码成α在线观看| 精品久久久久久国产免费了| 亚洲精品乱码久久久久久蜜桃不卡| 精品一卡2卡三卡4卡免费视频| 亚洲电影一区二区| 久久精品免费一区二区| 日韩亚洲国产高清免费视频| 韩国免费三片在线视频| 成人午夜免费视频| 亚洲精品亚洲人成在线观看| 久久久久久国产精品免费免费男同 | 性色午夜视频免费男人的天堂 | 18禁网站免费无遮挡无码中文| 亚洲无码一区二区三区| 男女交性永久免费视频播放| 免费一级全黄少妇性色生活片| 亚洲综合无码AV一区二区 | 四虎在线免费视频| 亚洲国产日韩a在线播放| 亚洲va中文字幕无码| 你懂得的在线观看免费视频| 精品亚洲aⅴ在线观看| 成年女人喷潮毛片免费播放| 美女扒开屁股让男人桶爽免费| 亚洲伊人久久精品影院|