(編輯后轉(zhuǎn)載原文出處不詳)
(程序中涉及所有源碼CSLib)
1 進(jìn)程地址空間
- 地址空間
軟件的進(jìn)程運(yùn)行于32位系統(tǒng)上,其尋址位也是32位赊颠,能表示的空間是2^32=4G,范圍從0x0000 0000~0xFFFF FFFF。 - NULL指針?lè)謪^(qū)
- 范圍:0x0000 0000~0x0000 FFFF
- 作用:保護(hù)內(nèi)存非法訪問(wèn)
- 例子:分配內(nèi)存時(shí)爸舒,如果由于某種原因分配不成功,則返回空指針0x00000000稿蹲;當(dāng)用戶繼續(xù)使用比如改寫數(shù)據(jù)時(shí)扭勉,系統(tǒng)將因?yàn)榘l(fā)生訪問(wèn)違規(guī)而退出。
那么苛聘,為什么需要那么大的區(qū)域呢剖效,一個(gè)地址值不就行了嗎嫉入?我在想,是不是因?yàn)椴蛔?或16位的程序運(yùn)行于32位的系統(tǒng)上呢璧尸?咒林!因?yàn)镹ULL分區(qū)剛好范圍是16的進(jìn)程空間。
- 獨(dú)享用戶分區(qū)
- 范圍:0x0001 0000~0x7FFE FFFF
- 作用:進(jìn)程只能讀取或訪問(wèn)這個(gè)范圍的虛擬地址爷光;超越這個(gè)范圍的行為都會(huì)產(chǎn)生違規(guī)退出垫竞。
- 例子:
程序的二進(jìn)制代碼中所用的地址大部分將在這個(gè)范圍,所有exe和dll文件都加載到這里蛀序。每個(gè)進(jìn)程將近2G的空間是獨(dú)享的欢瞪。 - 注意:如果在boot.ini上設(shè)置了/3G,這個(gè)區(qū)域的范圍從2G擴(kuò)大為3G:0x0001 0000~0xBFFE FFFF
- 共享內(nèi)核分區(qū)
- 范圍:0x8000 0000~0xFFFF FFFF
- 作用:這個(gè)空間是供操作系統(tǒng)內(nèi)核代碼徐裸、設(shè)備驅(qū)動(dòng)程序遣鼓、設(shè)備I/O高速緩存、非頁(yè)面內(nèi)存池的分配重贺、進(jìn)程目表和頁(yè)表等骑祟。
- 例子:這段地址各進(jìn)程是可以共享的。
- 注意:如果在boot.ini上設(shè)置了/3G气笙,這個(gè)區(qū)域的范圍從2G縮小為1G:0xC000 0000~0xFFFF FFFF次企。
通過(guò)以上分析,可以知道潜圃,如果系統(tǒng)有n個(gè)進(jìn)程缸棵,它所需的虛擬空間是:2G*n+2G (內(nèi)核只需2G的共享空間)。
地址映射
區(qū)域
區(qū)域指的是上述地址空間中的一片連續(xù)地址谭期。區(qū)域的大小必須是粒度(64k)的整數(shù)倍堵第,不是的話系統(tǒng)自動(dòng)處理成整數(shù)倍。不同CPU粒度大小是不一樣的隧出,大部分都是64K踏志。
區(qū)域的狀態(tài)有:空閑、私有鸳劳、映射狰贯、映像。
在你的應(yīng)用程序中赏廓,申請(qǐng)空間的過(guò)程稱作保留(預(yù)訂)涵紊,可以用VirtualAlloc;刪除空間的過(guò)程為釋放幔摸,可以用VirtualFree摸柄。
在程序里預(yù)訂了地址空間以后,你還不可以存取數(shù)據(jù)既忆,因?yàn)槟氵€沒(méi)有付錢驱负,沒(méi)有真實(shí)的RAM和它關(guān)聯(lián)嗦玖。
這時(shí)候的區(qū)域狀態(tài)是私有;
默認(rèn)情況下跃脊,區(qū)域狀態(tài)是空閑宇挫;
當(dāng)exe或DLL文件被映射進(jìn)了進(jìn)程空間后,區(qū)域狀態(tài)變成映像酪术;
當(dāng)一般數(shù)據(jù)文件被映射進(jìn)了進(jìn)程空間后器瘪,區(qū)域狀態(tài)變成映射。物理存儲(chǔ)器
Windows各系列支持的內(nèi)存上限是不一樣的绘雁,從2G到64G不等橡疼。理論上32位CPU,硬件上只能支持4G內(nèi)存的尋址庐舟;能支持超過(guò)4G的內(nèi)存只能靠其他技術(shù)來(lái)彌補(bǔ)欣除。順便提一下,Windows個(gè)人版只能支持最大2G內(nèi)存挪略,Intel使用Address Windows Extension (AWE) 技術(shù)使得尋址范圍為236=64G历帚。當(dāng)然,也得操作系統(tǒng)配合瘟檩。
內(nèi)存分配的最小單位是4K或8K抹缕,一般來(lái)說(shuō)澈蟆,根據(jù)CPU不同而不同墨辛,后面你可以看到可以通過(guò)系統(tǒng)函數(shù)得到區(qū)域粒度和頁(yè)面粒度。頁(yè)文件
頁(yè)文件是存在硬盤上的系統(tǒng)文件趴俘,它的大小可以在系統(tǒng)屬性里面設(shè)置睹簇,它相當(dāng)于物理內(nèi)存,所以稱為虛擬內(nèi)存寥闪。事實(shí)上太惠,它的大小是影響系統(tǒng)快慢的關(guān)鍵所在,如果物理內(nèi)存不多的情況下疲憋。
每頁(yè)的大小和上述所說(shuō)內(nèi)存分配的最小單位是一樣的凿渊,通常是4K或8K。-
映射過(guò)程
進(jìn)程地址空間的地址是虛擬地址缚柳,也就是說(shuō)埃脏,當(dāng)取到指令時(shí),需要把虛擬地址轉(zhuǎn)化為物理地址才能夠存取數(shù)據(jù)秋忙。這個(gè)工作通過(guò)頁(yè)目和頁(yè)表進(jìn)行彩掐。
從圖中可以看出,頁(yè)目大小為4K灰追,其中每一項(xiàng)(32位)保存一個(gè)頁(yè)表的物理地址堵幽;每個(gè)頁(yè)表大小為4K狗超,其中每一項(xiàng)(32位)保存一個(gè)物理頁(yè)的物理地址,一共有1024個(gè)頁(yè)表朴下。利用這4K+4K*1K=4.4M的空間可以表示進(jìn)程的1024X1024X(一頁(yè)4K) =4G的地址空間努咐。
進(jìn)程空間中的32位地址如下:
總結(jié):1k=2^10 1G=1024M=210M=220K
頁(yè)目數(shù):1k
頁(yè)表數(shù):1k
所以共有1k1k=1024k個(gè)頁(yè)
每個(gè)頁(yè)中有4k個(gè)地址 所以總尋址范圍是:
1024k X 4k=2^10 X 2^10 X 2^10 X 4 = 4G
高10位用來(lái)找到1024個(gè)頁(yè)目項(xiàng)中的一項(xiàng),取出頁(yè)表的物理地址后殴胧,利用中10位來(lái)得到頁(yè)表項(xiàng)的值麦撵,根據(jù)這個(gè)值得到物理頁(yè)的地址,由于一頁(yè)有4K大小溃肪,利用低12位得到單元地址免胃,這樣就可以訪問(wèn)這個(gè)內(nèi)存單元了。
每個(gè)進(jìn)程都有自己的一個(gè)頁(yè)目和頁(yè)表惫撰,那么羔沙,剛開(kāi)始進(jìn)程是怎么找到頁(yè)目所在的物理頁(yè)呢?答案是CPU的CR3寄存器會(huì)保存當(dāng)前進(jìn)程的頁(yè)目物理地址厨钻。
當(dāng)進(jìn)程被創(chuàng)建時(shí)扼雏,同時(shí)需要?jiǎng)?chuàng)建頁(yè)目和頁(yè)表,一共需要4.4M夯膀。在進(jìn)程的空間中诗充,0xC030 0000~0xC030 0FFF是用來(lái)保存頁(yè)目的4k空間。*
0xC000 0000~0xC03F FFFF是用來(lái)保存頁(yè)表的4M空間诱建。
也就是說(shuō)程序里面訪問(wèn)這些地址你是可以讀取頁(yè)目和頁(yè)表的具體值的(要工作在內(nèi)核方式下)蝴蜓。
至于說(shuō),頁(yè)目和頁(yè)表是保存在物理內(nèi)存還是頁(yè)文件中俺猿,我覺(jué)得茎匠,頁(yè)目比較常用,應(yīng)該在物理內(nèi)存的概率大點(diǎn)押袍,頁(yè)表需要時(shí)再?gòu)捻?yè)文件導(dǎo)入物理內(nèi)存中诵冒。
頁(yè)目項(xiàng)和頁(yè)表項(xiàng)是一個(gè)32位的值,當(dāng)頁(yè)目項(xiàng)第0位為1時(shí)谊惭,表明頁(yè)表已經(jīng)在物理內(nèi)存中汽馋;當(dāng)頁(yè)表項(xiàng)第0位為1時(shí),表明訪問(wèn)的數(shù)據(jù)已經(jīng)在內(nèi)存中圈盔。還有很多數(shù)據(jù)是否已經(jīng)被改變豹芯,是否可讀寫等標(biāo)志。另外药磺,當(dāng)頁(yè)目項(xiàng)第7位為1時(shí)告组,表明這是一個(gè)4M的頁(yè)面,這值已經(jīng)是物理頁(yè)地址癌佩,用虛擬地址的低22位作為偏移量木缝。還有很多:數(shù)據(jù)是否已經(jīng)被改變便锨、是否可讀寫等標(biāo)志。 例子
編寫生成軟件程序exe
軟件描述如下:
Main ()
{
1:定義全局變量
2:處理函數(shù)邏輯(Load 所需DLL庫(kù)我碟,調(diào)用方法處理邏輯)
3:定義并實(shí)現(xiàn)各種方法(方法含有局部變量)
4:程序結(jié)束
}
將程序編譯放案,生成exe文件,附帶所需的DLL庫(kù)矫俺。exe文件格式
exe文件有自己的格式吱殉,有若干節(jié)(section):.text用來(lái)放二進(jìn)制代碼(exe或dll);.data用來(lái)放各種全局?jǐn)?shù)據(jù)厘托。
.text
指令1:move a, b
指令2:add a, b
…
.data
數(shù)據(jù)1:a=2
數(shù)據(jù)2:b=1
…
這些地址都是虛擬地址友雳,也就是進(jìn)程的地址空間。-
運(yùn)行exe程序
- 建立進(jìn)程:
運(yùn)行這個(gè)exe程序時(shí)铅匹,系統(tǒng)會(huì)創(chuàng)建一個(gè)進(jìn)程押赊,建立進(jìn)程控制塊PCB,生成進(jìn)程頁(yè)目和頁(yè)表包斑,放到PCB中流礁。 - 數(shù)據(jù)對(duì)齊:
數(shù)據(jù)的內(nèi)存地址除以數(shù)據(jù)的大小,余數(shù)為0時(shí)說(shuō)明數(shù)據(jù)是對(duì)齊的÷薹幔現(xiàn)在的編譯器編譯時(shí)就考慮數(shù)據(jù)對(duì)齊的問(wèn)題神帅,生成exe文件后,數(shù)據(jù)基本上是對(duì)齊的萌抵,CPU運(yùn)行時(shí)找御,寄存器有標(biāo)志標(biāo)識(shí)CPU是否能夠自動(dòng)對(duì)齊數(shù)據(jù),如果遇到不能對(duì)齊的情況谜嫉,或者通過(guò)兩次訪問(wèn)內(nèi)存萎坷,或者通知操作系統(tǒng)處理凹联。
要注意的是沐兰,如果數(shù)據(jù)沒(méi)有對(duì)齊,CPU處理的效率是很低的蔽挠。 - 文件映射:
系統(tǒng)不會(huì)將整個(gè)exe文件和所有的DLL文件裝載進(jìn)物理內(nèi)存中住闯,同時(shí)它也不會(huì)裝載進(jìn)頁(yè)面文件中。相反澳淑,它會(huì)建立文件映射比原,也就是利用exe本身當(dāng)作頁(yè)面文件。系統(tǒng)將部分二進(jìn)制代碼裝載進(jìn)內(nèi)存杠巡,分配頁(yè)面給它量窘。
假設(shè)分配了一個(gè)頁(yè)面,物理地址為0x0232FFF1氢拥。其中裝載的一個(gè)指令虛擬地址為0x4000 1001=0100 0000 00 0000 0000 01 0000 0000 0001蚌铜。一個(gè)頁(yè)面有4K锨侯,系統(tǒng)會(huì)將指令保存在低12位0x0001的地址處。同時(shí)冬殃,系統(tǒng)根據(jù)高10位0x0100找到頁(yè)目項(xiàng)囚痴,如果沒(méi)有關(guān)聯(lián)的頁(yè)表,系統(tǒng)會(huì)生成一個(gè)頁(yè)表审葬,分配一個(gè)物理頁(yè)深滚;然后,根據(jù)中10位0x0001找到表項(xiàng)涣觉,將物理地址0x0232 FFF1存進(jìn)去痴荐。 - 執(zhí)行過(guò)程:
執(zhí)行時(shí),當(dāng)系統(tǒng)拿到一個(gè)虛擬地址官册,就根據(jù)頁(yè)目和頁(yè)表找到數(shù)據(jù)的地址蹬昌,根據(jù)頁(yè)目上的值可以判斷頁(yè)表是在頁(yè)文件中還是在內(nèi)存中;
如果在頁(yè)文件中攀隔,會(huì)將頁(yè)面導(dǎo)入內(nèi)存皂贩,更新頁(yè)目項(xiàng)。讀取頁(yè)表項(xiàng)的值后昆汹,可以判斷數(shù)據(jù)頁(yè)文件中還是在物理內(nèi)存中明刷;如果在頁(yè)文件中,會(huì)導(dǎo)入到內(nèi)存中满粗,更新頁(yè)表項(xiàng)辈末。最終,拿到了數(shù)據(jù)映皆。
在分配物理頁(yè)的過(guò)程中挤聘,系統(tǒng)會(huì)根據(jù)內(nèi)存分配的狀況適當(dāng)淘汰暫時(shí)不用的頁(yè)面,如果頁(yè)面內(nèi)容改變了(通過(guò)頁(yè)表項(xiàng)的標(biāo)志位)捅彻,保存到頁(yè)文件中组去,系統(tǒng)會(huì)維護(hù)內(nèi)存與頁(yè)文件的對(duì)應(yīng)關(guān)系。
由于將exe文件當(dāng)作內(nèi)存映射文件步淹,當(dāng)需要改變數(shù)據(jù)从隆,如更改全局變量的值時(shí),利用Copy-On-Write的機(jī)制缭裆,重新生成頁(yè)文件键闺,將結(jié)果保存在這個(gè)頁(yè)文件中,原來(lái)的頁(yè)文件還是需要被其他進(jìn)程實(shí)例使用的澈驼。
在清楚了指令和數(shù)據(jù)是如何導(dǎo)入內(nèi)存辛燥,如何找到它們的情況下,剩下的就是CPU不斷的取指令、運(yùn)行挎塌、保存數(shù)據(jù)的過(guò)程了畅铭,當(dāng)進(jìn)程結(jié)束后,系統(tǒng)會(huì)清空之前的各種結(jié)構(gòu)勃蜘、釋放相關(guān)的物理內(nèi)存和刪除頁(yè)文件硕噩。
- 建立進(jìn)程:
2 內(nèi)存狀態(tài)查詢
-
系統(tǒng)信息
Windows提供API可以查詢系統(tǒng)內(nèi)存的一些屬性,有時(shí)候我們需要獲取一些頁(yè)面大小缭贡、分配粒度等屬性炉擅,在分配內(nèi)存時(shí)用的上。
可以看出阳惹,頁(yè)面大小是4K谍失,區(qū)域分配粒度是64K,進(jìn)程用戶區(qū)是0x00010000~0x7FFEFFFF
- 內(nèi)存狀態(tài)
-
內(nèi)存狀態(tài)可以獲取總內(nèi)存和可用內(nèi)存莹汤,包括頁(yè)文件和物理內(nèi)存快鱼。
可以看出,總物理內(nèi)存是4G纲岭,可用物理內(nèi)存是4G抹竹,總頁(yè)文件是4G,這個(gè)是包含物理內(nèi)存的頁(yè)文件止潮;可用頁(yè)文件是4G窃判。這里還標(biāo)識(shí)了總進(jìn)程空間,還有可用的進(jìn)程空間,程序只用了22兆的內(nèi)存空間喇闸。這里說(shuō)的都是大約數(shù)袄琳。
內(nèi)存繁忙程序是標(biāo)識(shí)當(dāng)前系統(tǒng)內(nèi)存管理的繁忙程序,從0到100燃乍,其實(shí)用處不大唆樊。 -
在函數(shù)里面靜態(tài)分配一些內(nèi)存后,看看究竟發(fā)生什么
可以看出刻蟹,物理內(nèi)存逗旁、可用頁(yè)文件和進(jìn)程空間都沒(méi)有損耗。因?yàn)榫植孔兞渴欠峙湓诰€程堆棧里面的座咆,每個(gè)線程系統(tǒng)都會(huì)建立一個(gè)默認(rèn)1M大小的堆棧給線程函數(shù)調(diào)用使用痢艺。如果分配超過(guò)1M,就會(huì)出現(xiàn)堆棧溢出介陶。
-
在函數(shù)里面動(dòng)態(tài)分配300M的內(nèi)存后,看看究竟發(fā)生什么
動(dòng)態(tài)分配情況下色建,系統(tǒng)分配直到內(nèi)存頁(yè)文件使用完為止哺呜,當(dāng)然,系統(tǒng)要留一下系統(tǒng)使用的頁(yè)面箕戳。
- 進(jìn)程區(qū)域地址查詢
在給定一個(gè)進(jìn)程空間的地址后某残,可以查詢它所在區(qū)域和相鄰頁(yè)面的狀態(tài)国撵,包括頁(yè)面保護(hù)屬性、存儲(chǔ)器類型等玻墅。 -
C++靜態(tài)分配了兩次內(nèi)存介牙,一次是4K大一點(diǎn),一個(gè)是900K左右澳厢。
說(shuō)明:
1 區(qū)域基地址指的是給定地址所在的進(jìn)程空間區(qū)域环础;
2 鄰近頁(yè)面狀態(tài)指的是與給定地址所在頁(yè)面狀態(tài)相同頁(yè)面的屬性:
MEM_FREE(空閑=65536)
MEM_RESERVE(保留=8192)
MEM_COMMIT(提交=4096)
3 區(qū)域保護(hù)屬性指的是區(qū)域初次被保留時(shí)被賦予的保護(hù)屬性:
PAGE_READONLY(2)
PAGE_READWRITE(4)
PAGE_WRITECOPY(8)
PAGE_EXECUTE_WRITECOPY(128)
4 頁(yè)面基地址指的是給定地址所在頁(yè)面的基地址。
5 從頁(yè)面基地址開(kāi)始的區(qū)域頁(yè)面的大小剩拢,指的是與給定地址所在頁(yè)面狀態(tài)线得、保護(hù)屬性相同的頁(yè)面。
6 鄰近頁(yè)面物理存儲(chǔ)器類型指的是與給定地址所在頁(yè)面相同的存儲(chǔ)器類型徐伐,包括: MEM_PRIVATE(頁(yè)文件=131072)
MEM_MAPPED(文件映射=262144)
MEM_IMAGE(exe映像=16777216)
7 頁(yè)面保護(hù)屬性指的是頁(yè)面被指定的保護(hù)屬性贯钩,在區(qū)域保護(hù)屬性指定后更新。
8 如前所說(shuō)办素,這是在堆棧區(qū)域0x0004 0000里分配的角雷,后分配的地址arrayB反而更小,符合堆棧的特性性穿。arrayA和arrayB它們處于不同的頁(yè)面谓罗。頁(yè)面都受頁(yè)文件支持,并且區(qū)域都是提交的季二,是系統(tǒng)在線程創(chuàng)建時(shí)提交的檩咱。 -
C++動(dòng)態(tài)分配了兩次內(nèi)存,一次是1K大一點(diǎn)胯舷,一個(gè)是64K左右刻蚯。所以應(yīng)該不會(huì)在一個(gè)區(qū)域。
這里是動(dòng)態(tài)分配桑嘶,dynamicA和dynamicB處于兩個(gè)不同的區(qū)域炊汹;同樣,頁(yè)面都受頁(yè)文件支持逃顶,并且區(qū)域都是提交的讨便。