內(nèi)存管理概述中,主要是以Linux v2.6.11為例進(jìn)行分析的,但是計(jì)算技術(shù)在不斷發(fā)展,新的存儲(chǔ)架構(gòu)、新的指令集架構(gòu)、新的SoC架構(gòu)等都對(duì)物理內(nèi)存模型的抽象提出了更高要求。為此,必須抽象一種完全獨(dú)立于硬件架構(gòu)的物理內(nèi)存模型。
1 NUMA和UMA
第一個(gè)要解決的問題就是非一致性內(nèi)存訪問(NUMA)。對(duì)于多核和多內(nèi)存卡插槽的機(jī)器,內(nèi)存位于不同的分組中,那么與處理器的距離不同,就會(huì)產(chǎn)生訪問時(shí)間的差異。比如,可以將一組內(nèi)存更靠近CPU,另一組內(nèi)存更靠近外設(shè),作為訪問外設(shè)的DMA內(nèi)存。
這樣的每個(gè)分組稱為一個(gè)節(jié)點(diǎn)(node)。不管是NUMA架構(gòu),還是UMA架構(gòu),Linux提供了一個(gè)統(tǒng)一的數(shù)據(jù)結(jié)構(gòu)struct pglist_data表示節(jié)點(diǎn)信息。該列表中是類型為pg_data_t的數(shù)據(jù)結(jié)構(gòu),描述一個(gè)具體的節(jié)點(diǎn),該數(shù)據(jù)結(jié)構(gòu)可以通過NODE_DATA(nid)引用,這兒nid表示節(jié)點(diǎn)的ID。
對(duì)于NUMA架構(gòu),node數(shù)據(jù)結(jié)構(gòu)是由特定于架構(gòu)的代碼在boot階段分配的。通常,這些數(shù)據(jù)結(jié)構(gòu)分配的就是本地的內(nèi)存組(也就是離它們近的內(nèi)存)。對(duì)于UMA體系結(jié)構(gòu),只使用一個(gè)名為cong_page_data的靜態(tài)pg_data_t結(jié)構(gòu)。節(jié)點(diǎn)將在下一節(jié)討論。
2 內(nèi)存分區(qū)(memory zone)
節(jié)點(diǎn)內(nèi)的物理內(nèi)存被劃分為一個(gè)或多個(gè)區(qū),這樣的區(qū)稱為Zone。這些內(nèi)存分區(qū)的劃分通常由硬件架構(gòu)訪問物理內(nèi)存的限制決定。ZONE在內(nèi)核中的數(shù)據(jù)類型是zone_t,結(jié)構(gòu)是zone。zone的分類類型如下所示:
ZONE_DMA和ZONE_DMA32
當(dāng)外設(shè)無法DMA訪問所有可尋址內(nèi)存(ZONE_NORMAL)時(shí),使用ZONE_DMA和ZONE_DMA32。ZONE_DMA32用于覆蓋整個(gè)32位地址空間的架構(gòu)上。ZONE_DMA留給具有較小DMA尋址限制的區(qū)域。這種區(qū)別很重要,因?yàn)槎xZONE_DMA32時(shí)假定使用32位DMA掩碼。某些64位平臺(tái)可能需要2個(gè)區(qū)域,因?yàn)樗鼈冎С志哂胁煌珼MA尋址限制的外設(shè)。雖然近些年提供了更好、更強(qiáng)大的接口分配DMA內(nèi)存(使用通用設(shè)備的動(dòng)態(tài)DMA映射),但ZONE_DMA和ZONE_DMA32仍然表示對(duì)其訪問方式受限制的內(nèi)存。根據(jù)架構(gòu)不同,可以使用CONFIG_ZONE_DMA和CONFIG_ZONE_DMA32配置選項(xiàng),在構(gòu)建內(nèi)核時(shí),禁用這些內(nèi)存區(qū)域。
ZONE_NORMAL
普通內(nèi)存,內(nèi)核一直可以訪問的內(nèi)存區(qū)。如果DMA設(shè)備支持向所有可尋址內(nèi)存的傳輸,則可以對(duì)這些內(nèi)存頁(yè)執(zhí)行DMA操作。ZONE_NORMAL總是使能。
ZONE_HIGHMEM
是內(nèi)核頁(yè)表中永久映射未覆蓋的物理內(nèi)存部分。這個(gè)區(qū)域中的內(nèi)存只能由內(nèi)核使用臨時(shí)映射訪問。該區(qū)域僅在某些32位體系結(jié)構(gòu)上可用,并通過CONFIG_HIGHMEM啟用。
ZONE_MOVABLE
與ZONE_NORMAL類似。不同之處是ZONE_MOVABLE區(qū)的內(nèi)存頁(yè)是可移動(dòng)的。也就是說,在這些內(nèi)存頁(yè)的虛擬地址不改變的情況下,他們的內(nèi)容可以在不同的物理內(nèi)存頁(yè)之間搬運(yùn)。ZONE_MOVABLE通常在內(nèi)存熱插拔期間進(jìn)行填充,但也可以在boot階段,使用kernelcore、movablecore和movable_node命令行參數(shù)之一進(jìn)行填充。具體可以參考內(nèi)存頁(yè)遷移和熱插拔
ZONE_DEVICE
表示駐留在PMEM和GPU等設(shè)備上的內(nèi)存。它和內(nèi)存ZONE類型有著不同,它的存在是為了設(shè)備驅(qū)動(dòng)程序的物理內(nèi)存范圍提供page和內(nèi)存映射服務(wù)。ZONE_DEVICE可以通過配置選項(xiàng)CONFIG_ZONE_DEVICE使能。設(shè)備內(nèi)存熱插拔支持在mem_map中建立PMEM或其它設(shè)備驅(qū)動(dòng)程序發(fā)現(xiàn)的內(nèi)存區(qū)域。這允許pfn_to_page()查找“設(shè)備物理”地址,這是在O_DIRECT操作中使用DAX映射所需要的。
需要注意的是,許多內(nèi)核操作只能使用ZONE_NORMAL進(jìn)行,因此它是性能最關(guān)鍵的ZONE區(qū)。
節(jié)點(diǎn)和ZONE之間的關(guān)系由固件報(bào)告的物理內(nèi)存映射、內(nèi)存尋址的體系結(jié)構(gòu)約束和內(nèi)核命令行中的某些參數(shù)決定。
例如,對(duì)于具有2GB內(nèi)存的x86 UMA架構(gòu)內(nèi)核,整個(gè)內(nèi)存將位于節(jié)點(diǎn)0上,分為3個(gè)區(qū)域:“ZONE_DMA”、“ZONE_NORMAL”和“ZONE_HIGHMEM”:
02G +-------------------------------------------------------------+ |node0| +-------------------------------------------------------------+ 016M896M2G +----------+-----------------------+--------------------------+ |ZONE_DMA|ZONE_NORMAL|ZONE_HIGHMEM| +----------+-----------------------+--------------------------+
在ARM64機(jī)器上禁用ZONE_DMA并啟用ZONE_DMA32的同時(shí),使用movablecore = 80%參數(shù)啟動(dòng)內(nèi)核時(shí),在兩個(gè)節(jié)點(diǎn)之間平均分配16G內(nèi)存,則節(jié)點(diǎn)0上將有ZONE_DMA32,ZONE_NORMAL和ZONE_MOVABLE,節(jié)點(diǎn)1上將有ZONE_NORMAL和ZONE_MOVABLE:
1G9G17G +--------------------------------++--------------------------+ |node0||node1| +--------------------------------++--------------------------+ 1G4G4200M9G9320M17G +---------+----------+-----------++------------+-------------+ |DMA32|NORMAL|MOVABLE||NORMAL|MOVABLE| +---------+----------+-----------++------------+-------------+
內(nèi)存組也可以交替分配給節(jié)點(diǎn)。在下面的示例中,x86機(jī)器具有16G的內(nèi)存,分為4組,偶數(shù)組屬于節(jié)點(diǎn)0,奇數(shù)組屬于節(jié)點(diǎn)1:
04G8G12G16G +-------------++-------------++-------------++-------------+ |node0||node1||node0||node1| +-------------++-------------++-------------++-------------+ 016M4G +-----+-------++-------------++-------------++-------------+ |DMA|DMA32||NORMAL||NORMAL||NORMAL| +-----+-------++-------------++-------------++-------------+
在這種情況下,節(jié)點(diǎn)0將橫跨0→12G,節(jié)點(diǎn)1將橫跨4→16G。
3 節(jié)點(diǎn)
正如我們所提到的,內(nèi)存中的每個(gè)節(jié)點(diǎn)都由pg_data_t描述,pg_data_t是結(jié)構(gòu)體pglist_data的類型定義。在分配頁(yè)面時(shí),默認(rèn)情況下Linux使用節(jié)點(diǎn)本地分配策略,從離運(yùn)行CPU最近的節(jié)點(diǎn)分配內(nèi)存。由于進(jìn)程傾向于在同一個(gè)CPU上運(yùn)行,因此很可能會(huì)使用當(dāng)前節(jié)點(diǎn)的內(nèi)存。分配策略可以由用戶控制,詳見NUMA內(nèi)存策略。
大多數(shù)NUMA體系結(jié)構(gòu)維護(hù)一個(gè)指向node結(jié)構(gòu)的指針數(shù)組。實(shí)際的結(jié)構(gòu)是在boot過程的早期分配的,當(dāng)特定于體系結(jié)構(gòu)的代碼解析固件報(bào)告的物理內(nèi)存映射時(shí)。節(jié)點(diǎn)初始化的大部分在boot過程中稍晚的時(shí)候通過free_area_init()函數(shù)進(jìn)行,稍后將在初始化一節(jié)中描述。
除了node結(jié)構(gòu),內(nèi)核還維護(hù)一個(gè)名為node_states的nodemask_t位掩碼數(shù)組。這個(gè)數(shù)組中的每個(gè)位掩碼代表一組具有特定屬性的節(jié)點(diǎn),這些屬性由enum node_states定義:
N_POSSIBLE
節(jié)點(diǎn)可能在某個(gè)時(shí)間點(diǎn)在線。
N_ONLINE
節(jié)點(diǎn)在線。
N_NORMAL_MEMORY
節(jié)點(diǎn)具有常規(guī)內(nèi)存。
N_HIGH_MEMORY
節(jié)點(diǎn)具有常規(guī)內(nèi)存或高端內(nèi)存。當(dāng)CONFIG_HIGHMEM被禁用時(shí),別名為N_NORMAL_MEMORY。
N_MEMORY
節(jié)點(diǎn)具有內(nèi)存(常規(guī)、高、可移動(dòng)).
N_CPU
節(jié)點(diǎn)內(nèi)CPU數(shù)量。
對(duì)于具有上述屬性的每個(gè)節(jié)點(diǎn),將設(shè)置與node_states[
例如,節(jié)點(diǎn)2,具有常規(guī)內(nèi)存和CPU,位2將在下面設(shè)置:
node_states[N_POSSIBLE] node_states[N_ONLINE] node_states[N_NORMAL_MEMORY] node_states[N_HIGH_MEMORY] node_states[N_MEMORY] node_states[N_CPU]
關(guān)于nodemask的各種操作,請(qǐng)參考include/linux/nodemask.h。
有了節(jié)點(diǎn)之后,LRU列表、頁(yè)回收機(jī)制、交換區(qū)(kswapd)等等都需要在NONE分區(qū)之上添加處理。細(xì)節(jié)在此不再詳述。
審核編輯:湯梓紅
-
soc
+關(guān)注
關(guān)注
38文章
4065瀏覽量
217550 -
Linux
+關(guān)注
關(guān)注
87文章
11161瀏覽量
208467 -
內(nèi)存
+關(guān)注
關(guān)注
8文章
2942瀏覽量
73728 -
模型
+關(guān)注
關(guān)注
1文章
3058瀏覽量
48572
原文標(biāo)題:Linux內(nèi)核8.9-內(nèi)存管理進(jìn)階之物理內(nèi)存模型的演變
文章出處:【微信號(hào):嵌入式ARM和Linux,微信公眾號(hào):嵌入式ARM和Linux】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論