所謂進(jìn)程地址空間(process address space)囚戚,就是從進(jìn)程的視角看到的地址空間阿趁,是進(jìn)程運(yùn)行時所用到的虛擬地址的集合螺捐。
32位系統(tǒng)的進(jìn)程地址空間
以IA-32處理器為例请梢,其虛擬地址為32位忧额,因此其虛擬地址空間的范圍為4gb泌神,Linux系統(tǒng)將地址空間按3:1比例劃分良漱,其中用戶空間(user space)占3GB,內(nèi)核空間(kernel space)占1GB欢际。
假設(shè)物理內(nèi)存也是4GB(事實(shí)上母市,虛擬地址空間的范圍不一定需要和物理地址空間的大小相同),則虛擬地址空間和物理地址空間的轉(zhuǎn)換如下圖所示:
因?yàn)閮?nèi)核的虛擬地址空間只有1GB损趋,但它需要訪問整個4GB的物理空間患久,因此從物理地址0~896MB的部分(ZONE_DMA+ZONE_NORMAL),直接加上3GB的偏移(在Linux中用PAGE_OFFSET表示)浑槽,就得到了對應(yīng)的虛擬地址蒋失,這種映射方式被稱為線性/直接映射(Direct Map)。
而896M4GB的物理地址部分(ZONE_HIGHMEM)需要映射到(3G+896M)4GB這128MB的虛擬地址空間桐玻,顯然也按線性映射是不行的篙挽。
采用的是做法是,ZONE_HIGHMEM中的某段物理內(nèi)存和這128M中的某段虛擬空間建立映射镊靴,完成所需操作后铣卡,需要斷開與這部分虛擬空間的映射關(guān)系链韭,以便ZONE_HIGHMEM中其他的物理內(nèi)存可以繼續(xù)往這個區(qū)域映射,即動態(tài)映射的方式煮落。
用戶空間的進(jìn)程只能訪問整個虛擬地址空間的03GB部分敞峭,不能直接訪問3G4GB的內(nèi)核空間部分,但出于對性能方面的考慮州邢,Linux中內(nèi)核使用的地址也是映射到進(jìn)程地址空間的(被所有進(jìn)程共享)儡陨,因此進(jìn)程的虛擬地址空間可視為整個4GB(雖然實(shí)際只有3GB)。
NUMA
所謂物理內(nèi)存量淌,就是安裝在機(jī)器上的骗村,實(shí)打?qū)嵉膬?nèi)存設(shè)備(不包括硬件cache),被CPU通過總線訪問呀枢。在多核系統(tǒng)中胚股,如果物理內(nèi)存對所有CPU來說沒有區(qū)別,每個CPU訪問內(nèi)存的方式也一樣裙秋,則這種體系結(jié)構(gòu)被稱為Uniform Memory Access(UMA)琅拌。
如果物理內(nèi)存是分布式的,由多個cell組成(比如每個核有自己的本地內(nèi)存)摘刑,那么CPU在訪問靠近它的本地內(nèi)存的時候就比較快进宝,訪問其他CPU的內(nèi)存或者全局內(nèi)存的時候就比較慢,這種體系結(jié)構(gòu)被稱為Non-Uniform Memory Access(NUMA)枷恕。
以上是硬件層面上的NUMA党晋,而作為軟件層面的Linux,則對NUMA的概念進(jìn)行了抽象徐块。即便硬件上是一整塊連續(xù)內(nèi)存的UMA未玻,Linux也可將其劃分為若干的node。同樣胡控,即便硬件上是物理內(nèi)存不連續(xù)的NUMA扳剿,Linux也可將其視作UMA。
所以昼激,在Linux系統(tǒng)中庇绽,你可以基于一個UMA的平臺測試NUMA上的應(yīng)用特性。從另一個角度橙困,UMA就是只有一個node的特殊NUMA敛劝,所以兩者可以統(tǒng)一用NUMA模型表示。
在NUMA系統(tǒng)中纷宇,當(dāng)Linux內(nèi)核收到內(nèi)存分配的請求時夸盟,它會優(yōu)先從發(fā)出請求的CPU本地或鄰近的內(nèi)存node中尋找空閑內(nèi)存,這種方式被稱作local allocation像捶,local allocation能讓接下來的內(nèi)存訪問相對底層的物理資源是local的上陕。
每個node由一個或多個zone組成(我們可能經(jīng)常在各種對虛擬內(nèi)存和物理內(nèi)存的描述中迷失桩砰,但以后你見到zone,就知道指的是物理內(nèi)存)释簿,每個zone又由若干page frames組成(一般page frame都是指物理頁面)亚隅。
Page Frame
雖然內(nèi)存訪問的最小單位是byte或者word,但MMU是以page為單位來查找頁表的庶溶,page也就成了Linux中內(nèi)存管理的重要單位煮纵。包括換出(swap out)、回收(relcaim)偏螺、映射等操作行疏,都是以page為粒度的。
因此套像,描述page frame的struct page自然成為了內(nèi)核中一個使用頻率極高酿联,非常重要的結(jié)構(gòu)體,來看下它是怎樣構(gòu)成的(為了講解需要并非最新內(nèi)核代碼):
struct page {
unsigned long flags;
atomic_t count;
atomic_t _mapcount;
struct list_head lru;
struct address_space *mapping;
unsigned long index;
...
}
- flags表示page frame的狀態(tài)或者屬性夺巩,包括和內(nèi)存回收相關(guān)的PG_active, PG_dirty, PG_writeback, PG_reserved, PG_locked, PG_highmem等贞让。其實(shí)flags是身兼多職的,它還有其他用途柳譬,這將在下文中介紹到喳张。
- count表示引用計數(shù)。當(dāng)count值為0時美澳,該page frame可被free掉销部;如果不為0,說明該page正在被某個進(jìn)程或者內(nèi)核使用人柿,調(diào)用page_count()可獲得count值。
- _mapcount表示該page frame被映射的個數(shù)忙厌,也就是多少個page table entry中含有這個page frame的PFN凫岖。
- lru是"least recently used"的縮寫,根據(jù)page frame的活躍程度(使用頻率)逢净,一個可回收的page frame要么掛在active_list雙向鏈表上哥放,要么掛在inactive_list雙向鏈表上,以作為頁面回收的選擇依據(jù)爹土,lru中包含的就是指向所在鏈表中前后節(jié)點(diǎn)的指針(參考這篇文章)甥雕。
- 如果一個page是屬于某個文件的(也就是在page cache中),則mapping指向文件inode對應(yīng)的address_space(這個結(jié)構(gòu)體雖然叫address_space胀茵,但并不是進(jìn)程地址空間里的那個address space)社露,index表示該page在文件內(nèi)的offset(以page size為單位)。
有了文件的inode和index琼娘,當(dāng)這個page的內(nèi)容需要和外部disk/flash上對應(yīng)的部分同步時峭弟,才可以找到具體的文件位置附鸽。如果一個page是anonymous的,則mapping指向表示swap cache的swapper_space瞒瘸,此時index就是swapper_space內(nèi)的offset坷备。
需要注意的是,struct page描述和管理的是這4KB的物理內(nèi)存情臭,它并不關(guān)注這段內(nèi)存中的數(shù)據(jù)變化省撑。
Zone
因?yàn)橛布南拗疲瑑?nèi)核不能對所有的page frames采用同樣的處理方法俯在,因此它將屬性相同的page frames歸到一個zone中竟秫。對zone的劃分與硬件相關(guān),對不同的處理器架構(gòu)是可能不一樣的朝巫。
比如在i386中鸿摇,一些使用DMA的設(shè)備只能訪問016MB的物理空間,因此將016MB劃分為了ZONE_DMA劈猿。ZONE_HIGHMEM則是適用于要訪問的物理地址空間大于虛擬地址空間拙吉,不能建立直接映射的場景。除開這兩個特殊的zone揪荣,物理內(nèi)存中剩余的部分就是ZONE_NORMAL了筷黔。