1, 介紹
我們可以把物理內(nèi)存簡單地看成一個大的數(shù)組,其中每個字節(jié)都可以通過物理地址進行訪問。
前面的文章《一文搞懂DDR SDRAM工作原理》介紹過物理內(nèi)存的物理結(jié)構(gòu),及怎么通過控制器、PHY讀、寫SDRAM芯片獲取、寫入數(shù)據(jù),讓我們明白物理內(nèi)存在硬件原理方面的實現(xiàn)是什么樣的。
在《一文搞懂CPU的工作原理》介紹過CPU訪問物理內(nèi)存的全過程,總結(jié)下來就是:
CPU寫物理內(nèi)存的過程:CPU先給出要寫入數(shù)據(jù)的物理地址對應(yīng)的虛擬地址,通過MMU轉(zhuǎn)化為物理地址,若cache中沒有命中,則將要寫入數(shù)據(jù)的物理地址放到系統(tǒng)總線上。DDR的控制器感受到總線上的地址信號以及寫控制信號,將物理地址從總線上讀出來,并等待數(shù)據(jù)的到達。CPU將數(shù)據(jù)發(fā)送到系統(tǒng)總線上,DDR控制器感受到總線上的數(shù)據(jù)信號,將數(shù)據(jù)從總線上讀取出來。DDR控制器通過物理地址找到相應(yīng)的存儲模塊,然后將數(shù)據(jù)寫入到物理地址對應(yīng)的存儲模塊。
CPU讀物理內(nèi)存的過程:CPU給出要讀數(shù)據(jù)的物理地址對應(yīng)的虛擬地址,通過MMU轉(zhuǎn)化為物理地址,若cache中沒有命中,則將物理地址放到系統(tǒng)總線上。DDR控制器感受到總線上的地址型號及讀控制信號,將物理地址從總線上讀取出來,DDR控制器根據(jù)物理地址找到存儲模塊中數(shù)據(jù)的位置,并從SDRAM芯片中取出物理地址對應(yīng)的數(shù)據(jù),DDR控制器將數(shù)據(jù)放到總線上,CPU從總線上獲取數(shù)據(jù),并存放到寄存器上。
之前已經(jīng)講述過CPU讀、寫物理內(nèi)存的過程,本文主要講述linux內(nèi)核是怎么管理物理內(nèi)存,包括物理內(nèi)存涉及的數(shù)據(jù)結(jié)構(gòu)、內(nèi)存模型、內(nèi)存架構(gòu)、物理內(nèi)存的管理流程。
2, 數(shù)據(jù)結(jié)構(gòu)
與物理內(nèi)存相關(guān)的數(shù)據(jù)結(jié)構(gòu)有內(nèi)存節(jié)點(pglist_data)、內(nèi)存管理區(qū)(zone)、物理頁面(page)、mem_map數(shù)組、頁表項(PTE)、頁幀號(PFN)、物理地址(paddress)。
Linux內(nèi)核通過struct page來管理物理內(nèi)存中的一個頁。內(nèi)核為每個物理頁定義了一個索引編號PFN(Page Frame Number,頁幀號),這個PFN與struct page是一一對應(yīng)的。通過page_to_pfn/pfn_to_page兩個宏實現(xiàn)物理頁和struct page之間的相互轉(zhuǎn)換。
3, 框架
3.1 內(nèi)存架構(gòu)
在當前的計算機、嵌入式系統(tǒng)中,以內(nèi)存為研究對象可以分成兩種架構(gòu)。一種是UMA(Uniform Memory Access,統(tǒng)一內(nèi)存訪問)架構(gòu),另外一種是NUMA(Non-Uniform Memory Access,非統(tǒng)一內(nèi)存訪問)架構(gòu)。
1) UMA內(nèi)存架構(gòu)
內(nèi)存可以被其他模塊統(tǒng)一尋址,有統(tǒng)一的結(jié)構(gòu)。目前,大部分嵌入式系統(tǒng)及計算機系統(tǒng)都采用UMA架構(gòu)。如上圖所示,是一個UMA架構(gòu)的系統(tǒng),有兩個cpu位于同一個cluster中,cpu分別有自己的L1D、L1I cache及L2 cache。兩個cpu共享L3 cache,通過系統(tǒng)總線可以訪問物理內(nèi)存DDR,SRAM、SSD等模塊,并且兩個CPU對物理內(nèi)存的訪問消耗是一樣的。這種訪問模式的處理器被成為SMP(Aymmetric Multiprocessing,對稱多處理器)
2) NUMA內(nèi)存架構(gòu)
系統(tǒng)中有多個內(nèi)存節(jié)點和多個cpu cluster,CPU訪問本地內(nèi)存節(jié)點的時間開銷最小,訪問遠端的內(nèi)存節(jié)點的時間開銷要大。如上圖所示,是一個NUMA架構(gòu)的系統(tǒng),其中cpu0、cpu1在cluster0中,與相應(yīng)的L1I/L1D cache、L2 cache、L3 cache及DDR組成node0節(jié)點。同樣的,CPU2、CPU3在cluster1中,與相應(yīng)的L1I/L1D cache、L2 cache、L3 cache及DDR組成node1節(jié)點。兩個node節(jié)點,通過UPI(Ultra Path Interconnect,超路徑互聯(lián))總線連接。CPU0可以通過這個UPI訪問遠端node1上的物理內(nèi)存,但是要比本地node0的內(nèi)存訪問慢得多。
3.2 內(nèi)存模型
內(nèi)核是以頁為單位使用struct page數(shù)據(jù)結(jié)構(gòu)來管理物理內(nèi)存的。內(nèi)核通過物理內(nèi)存模型來實現(xiàn)組織管理這些物理內(nèi)存頁,不同的物理內(nèi)存模型,應(yīng)對的場景及頁幀號與物理頁之間的計算方式也不一樣。
1) 平坦內(nèi)存模型:FLATMEM
Linux早期使用的物理內(nèi)存比較小,比如幾十MB,并且這些物理內(nèi)存是一片連續(xù)的存儲空間,這樣物理地址也是連續(xù)的,按固定頁大小劃分出來的物理頁也是連續(xù)的。Linux內(nèi)核會用一個mem_map全局數(shù)組來組織管理所有的物理頁,其中物理頁是通過struct page來管理,這樣每個數(shù)組的下標便是PFN。這種連續(xù)的物理內(nèi)存便是平坦內(nèi)存模型。
2) 非連續(xù)內(nèi)存模型:DISCONTIGMEM
對于PLATMEM適合管理一整塊連續(xù)的物理內(nèi)存,但是對于多塊非連續(xù)的物理內(nèi)存,若使用FLATMEM來管理,這時mem_map全局數(shù)組中會有不連續(xù)內(nèi)存地址區(qū)的內(nèi)存空洞,這會造成內(nèi)存空間的浪費。為了管理這種不連續(xù)的物理內(nèi)存,內(nèi)核引入了DISCONTIGMEM非連續(xù)內(nèi)存模型來管理,以便消除不連續(xù)的內(nèi)存地址空洞對mem_map全局數(shù)組造成的空間浪費。
DISCONTIGMEM非連續(xù)內(nèi)存模型的思路是:將物理內(nèi)存從宏觀上劃分成一個個節(jié)點node,但是微觀上還是以物理頁為單位,每個node節(jié)點管理一塊連續(xù)的物理內(nèi)存,這樣這些非連續(xù)的內(nèi)存,會以連續(xù)的內(nèi)存方式劃分到node節(jié)點中管理起來,這樣便可以避免內(nèi)存空洞造成的空間浪費。
3) 稀疏內(nèi)存模型:SPARSEMEM
由于物理內(nèi)存在使用的時候,會有很多空洞,這樣物理內(nèi)存存在多處不連續(xù)。如果利用上面講的DISCONTIGMEM內(nèi)存模型,會造成node眾多,這樣開銷就大了。為了能夠更靈活、更高效的、更小的管理連續(xù)物理內(nèi)存。SPARSEMEM系數(shù)內(nèi)存模型就是為了對粒度更小的連續(xù)內(nèi)存塊進行精細的管理,用于管理連續(xù)內(nèi)存塊的單元被稱為section。在內(nèi)存中用struct mem_section結(jié)構(gòu)體表示SPARSEMEM模型中的section。
由于section被用作管理小粒度的連續(xù)內(nèi)存塊,這些小的連續(xù)物理內(nèi)存在section中也是通過數(shù)組的方式被組織管理,其中mem_section結(jié)構(gòu)體中的section_mem_map指針用于指向section中管理連續(xù)內(nèi)存的page數(shù)組。SPARSEMEM內(nèi)存模型中的mem_section會存在放在一個全局的數(shù)組中,并且每個mem_section都可以在系統(tǒng)運行的時候進行內(nèi)存的offline/online,這樣便可以支持內(nèi)存的熱拔插。
4, 物理內(nèi)存初始化
4.1 內(nèi)存大小初始化
物理內(nèi)存的大小會在DTS(Device Tree Source,設(shè)備樹)中描述,如下dts的描述:
memory { device_type = "memory"; reg = <0x000000000 0x80000000 0x00000000 0x40000000>; };
起始地址為0x80000000,大小為0x40000000
內(nèi)存在啟動的過程中,會解析上面的DTS,相應(yīng)的調(diào)用過程如下:
4.2 memblock內(nèi)存分配器
在伙伴系統(tǒng)沒有初始化前,在內(nèi)核中需要一套機制管理內(nèi)存的申請與釋放。在啟動的過程中,會解析設(shè)備樹中的memory節(jié)點,把所有物理內(nèi)存添加到memblock中。后面會通過一篇文章講解memblock分配器。這里先把結(jié)構(gòu)體及函數(shù)接口列出來。
4.3 ZONE初始化
在對頁表初始化后,內(nèi)核就會對內(nèi)存進行管理。內(nèi)核會將這些物理內(nèi)存分配成不同的內(nèi)存管理區(qū)(ZONE),分別針對這些內(nèi)存管理區(qū)進行管理。
常見的內(nèi)存管理區(qū)如下:
ZONE_DMA:用于inter X86 ISA設(shè)備的DMA操作,范圍是0~16MB,ARM沒有這個內(nèi)存管理區(qū)。
ZONE_DMA32:用于最低4GB的內(nèi)存訪問的設(shè)備,如只支持32位的DMA設(shè)備。
ZONE_NORMAL:4GB 以后的物理內(nèi)存,用于線性映射物理內(nèi)存。若系統(tǒng)內(nèi)存小于4GB,則沒有這個內(nèi)存管理區(qū)。
ZONE_HIGHMEM:用于管理高端內(nèi)存,這些高端內(nèi)存是不能線性映射到內(nèi)核地址空間的。64位Linux是沒有這個內(nèi)存管理區(qū)的。
其中ZONE是利用struct zone數(shù)據(jù)結(jié)構(gòu)進行管理的,zone數(shù)據(jù)結(jié)構(gòu)經(jīng)常會被訪問,因此為了提升性能,這個數(shù)據(jù)結(jié)構(gòu)要求以L1高速緩存對齊。數(shù)據(jù)結(jié)構(gòu)zone中關(guān)鍵的成員如下:
Watermark:每個zone在系統(tǒng)啟動時會計算出3個水位,分別是WMARK_MIN(最低警戒水位)、WMARK_LOW(低水位)、WMARK_HIGH(高水位),這些在頁面分配器和kswapd頁面回收中會用到。
Lowemem_reserve:防止頁面分配器過渡使用低端zone的內(nèi)存。
Zone_pgdat:指向內(nèi)存節(jié)點。
Pageset:用于維護每個cpu上的一些列頁面,以減少自旋鎖的使用
Zone_start_pfn:zone的起始頁幀號。
Managed_pages:zone中被伙伴系統(tǒng)管理的頁面數(shù)量。
Spanned_pages:zone中包含的頁面數(shù)量。
Present_pages:zone里實際管理的頁面數(shù)量。對于一些架構(gòu)來說,它和spanned_pages數(shù)量一致。
Free_area:伙伴系統(tǒng)核心的數(shù)據(jù)結(jié)構(gòu),管理空閑也快鏈表的數(shù)組。
Lock:并行訪問時用于保護zone的自旋鎖。
Lruvec:LRU鏈表集合。
4.4 伙伴系統(tǒng)
內(nèi)核啟動完成后,物理內(nèi)存的頁面就要添加到伙伴系統(tǒng)中來管理了。伙伴系統(tǒng)(buddy system)是操作系統(tǒng)中常用的動態(tài)內(nèi)存管理方法。用戶提出申請時,分配一個大小合適的物理內(nèi)存,當用戶釋放后,回收相應(yīng)的物理內(nèi)存。后面會專門寫一篇介紹伙伴系統(tǒng)的文章,這里只做簡單的介紹。
在伙伴系統(tǒng)中,內(nèi)存塊的大小是2的order次冪個頁幀。Linux中order的最大值是11。伙伴系統(tǒng)大致的思想是,將所有空閑的物理內(nèi)存頁面分組成11個內(nèi)存塊的鏈表,每個內(nèi)存塊的鏈表分別包含1、2、4、8、16、32、…、1024個連續(xù)的物理頁面。1024個物理頁面對應(yīng)著4MB大小的連續(xù)物理內(nèi)存。
由上一節(jié)我們了解到,物理內(nèi)存在linux中分出了幾個ZONE來管理空閑物理頁塊。ZONE可以根據(jù)內(nèi)核的配置來劃分。每個ZONE又是利用伙伴系統(tǒng)來管理。ZONE的數(shù)據(jù)結(jié)構(gòu)有一個free_area數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu)的大小是MAX_ORDER(11)。free_area數(shù)據(jù)結(jié)構(gòu)中包含了MIGRATE_TYPES個鏈表。可以理解成ZONE根據(jù)order的大小由0~(MAX_ORDER-1)個free_area,每個free_area根據(jù)MIGRATE_TYPES類型,由幾個相應(yīng)的鏈表組成。
審核編輯:劉清
-
控制器
+關(guān)注
關(guān)注
112文章
16416瀏覽量
178761 -
DDR
+關(guān)注
關(guān)注
11文章
712瀏覽量
65420 -
Linux
+關(guān)注
關(guān)注
87文章
11326瀏覽量
209962 -
LINUX內(nèi)核
+關(guān)注
關(guān)注
1文章
316瀏覽量
21688 -
MMU
+關(guān)注
關(guān)注
0文章
91瀏覽量
18325
原文標題:一文搞懂linux物理內(nèi)存管理
文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論