自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Linux內(nèi)核物理內(nèi)存模型:開(kāi)啟內(nèi)存管理之門(mén)

系統(tǒng) Linux
在 Linux 系統(tǒng)的龐大架構(gòu)里,內(nèi)存管理無(wú)疑是一塊關(guān)鍵基石。它肩負(fù)著保障系統(tǒng)穩(wěn)定運(yùn)行、實(shí)現(xiàn)資源高效利用以及提升應(yīng)用程序性能等多重重任,猶如一位幕后英雄,默默支撐著整個(gè)系統(tǒng)的運(yùn)轉(zhuǎn)。

在計(jì)算機(jī)的世界里,內(nèi)存就像一座神秘的大廈,而 Linux 內(nèi)核的物理內(nèi)存模型則是這座大廈的基石。今天,我們將一起走進(jìn) Linux 內(nèi)核物理內(nèi)存模型的世界,探索它是如何管理和分配內(nèi)存資源的。這就像是一場(chǎng)穿越時(shí)空的旅行,從最基礎(chǔ)的內(nèi)存架構(gòu)到復(fù)雜的內(nèi)存管理機(jī)制,每一步都將為我們揭開(kāi) Linux 內(nèi)核的神秘面紗。

無(wú)論是對(duì)計(jì)算機(jī)技術(shù)感興趣的初學(xué)者,還是想要深入了解內(nèi)核的專(zhuān)業(yè)人士,都能在這里找到屬于自己的收獲。讓我們開(kāi)啟這場(chǎng)內(nèi)存管理的奇妙之旅,去發(fā)現(xiàn) Linux 內(nèi)核物理內(nèi)存模型的無(wú)限魅力。

一、物理內(nèi)存模型概述

在 Linux 系統(tǒng)的龐大架構(gòu)里,內(nèi)存管理無(wú)疑是一塊關(guān)鍵基石。它肩負(fù)著保障系統(tǒng)穩(wěn)定運(yùn)行、實(shí)現(xiàn)資源高效利用以及提升應(yīng)用程序性能等多重重任,猶如一位幕后英雄,默默支撐著整個(gè)系統(tǒng)的運(yùn)轉(zhuǎn)。當(dāng)我們同時(shí)開(kāi)啟多個(gè)應(yīng)用程序,比如一邊聽(tīng)音樂(lè)、一邊瀏覽網(wǎng)頁(yè),還后臺(tái)運(yùn)行著文件下載任務(wù),此時(shí)內(nèi)存管理就要像一位精明的管家,合理分配內(nèi)存資源,讓每個(gè)程序都能順暢運(yùn)行,互不干擾。

這背后靠的就是內(nèi)存管理對(duì)進(jìn)程內(nèi)存空間的精細(xì)劃分與調(diào)度,確保每個(gè)進(jìn)程都有專(zhuān)屬的 “內(nèi)存領(lǐng)地”,避免數(shù)據(jù)混亂與沖突。再者,對(duì)于資源有限的嵌入式設(shè)備,如智能手環(huán)、智能家居控制器等,高效的內(nèi)存管理更是決定設(shè)備性能優(yōu)劣的關(guān)鍵。通過(guò)優(yōu)化內(nèi)存使用,系統(tǒng)能夠快速響應(yīng)操作指令,避免卡頓,為用戶(hù)帶來(lái)流暢體驗(yàn)。毫不夸張地說(shuō),深入探究 Linux 內(nèi)核內(nèi)存管理機(jī)制,是解鎖系統(tǒng)潛能、優(yōu)化應(yīng)用性能的必經(jīng)之路。而物理內(nèi)存模型作為內(nèi)存管理的根基,更是值得我們深入挖掘。

操作系統(tǒng)是構(gòu)建在硬件架構(gòu)之上的,Linux 自然也不能幸免。目前,主要有兩種類(lèi)型的物理內(nèi)存架構(gòu):UMA(Uniform Memory Access,一致性?xún)?nèi)存訪問(wèn))架構(gòu)和 NUMA (Non-Uniform Memory Access,非一致性?xún)?nèi)存訪問(wèn))架構(gòu)。UMA 將可用內(nèi)存以連續(xù)的方式組織起來(lái),系統(tǒng)中各 CPU 到內(nèi)存的距離相同,訪問(wèn)時(shí)間一致;NUMA 架構(gòu)將系統(tǒng)中的內(nèi)存和 CPU 分成不同的組(節(jié)點(diǎn)),每個(gè) CPU 訪問(wèn)本節(jié)點(diǎn)的內(nèi)存(稱(chēng)為本地內(nèi)存,local memory)比訪問(wèn)其它節(jié)點(diǎn)的內(nèi)存(稱(chēng)為非本節(jié)點(diǎn)內(nèi)存 non-local memory 或遠(yuǎn)端內(nèi)存 remote memory)速度要快。

在這兩種內(nèi)存架構(gòu)的基礎(chǔ)上,分為三種內(nèi)存模型,分別是:平坦內(nèi)存模型、非連續(xù)內(nèi)存模型稀疏內(nèi)存模型。平坦內(nèi)存模型對(duì)應(yīng)著內(nèi)核配置選項(xiàng) FLATMEM,非連續(xù)內(nèi)存模型對(duì)應(yīng)著內(nèi)核配置選項(xiàng) DISCONTIGMEM,稀疏內(nèi)存模型對(duì)應(yīng)著內(nèi)核配置選項(xiàng) SPARSEMEM 或者 SPARSEMEM_VMEMMAP。

二、體系架構(gòu):多樣的內(nèi)存布局選擇

2.1UMA 架構(gòu)

在單處理器時(shí)期,架構(gòu)如下圖所示:

圖片圖片

隨著多處理器時(shí)代的來(lái)臨,架構(gòu)演變成如下結(jié)構(gòu):

圖片圖片

在這種架構(gòu)下,所有的 CPU 位于總線的一側(cè),而所有的內(nèi)存條組成的整塊內(nèi)存位于總線的另一側(cè)。任何 CPU 想要訪問(wèn)內(nèi)存都要經(jīng)過(guò)總線,而且距離都是一樣的,這種架構(gòu)稱(chēng)為 SMP(Symmetric Multiprocessing,對(duì)稱(chēng)多處理器)架構(gòu)。在 SMP 架構(gòu)下,任何處理器訪問(wèn)內(nèi)存的距離是相同的,所以其訪問(wèn)內(nèi)存的速度是一致的。這種架構(gòu)也被成為基于 SMP 的 UMA (Uniform Memory Access,一致性?xún)?nèi)存訪問(wèn))架構(gòu)。

UMA 架構(gòu)的特點(diǎn)是簡(jiǎn)單,但是有一個(gè)顯著的缺點(diǎn):由于所有處理器訪問(wèn)內(nèi)存都要經(jīng)過(guò)總線,當(dāng)處理器數(shù)量很多時(shí),總線就會(huì)成為整個(gè)系統(tǒng)的瓶頸。

2.2NUMA 架構(gòu)

在現(xiàn)代多處理器系統(tǒng)的舞臺(tái)上,非一致內(nèi)存訪問(wèn)(NUMA)架構(gòu)宛如一顆閃耀的明星,占據(jù)著中高端服務(wù)器領(lǐng)域的主流地位。它的設(shè)計(jì)理念猶如一場(chǎng)對(duì)傳統(tǒng)內(nèi)存訪問(wèn)模式的革新,將物理內(nèi)存巧妙地劃分成多個(gè)獨(dú)立的節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)都緊密簇?fù)碇唤M處理器、本地內(nèi)存以及 I/O 設(shè)備,宛如一個(gè)個(gè)自給自足的 “小王國(guó)”。

以一臺(tái)配備 NUMA 架構(gòu)的高性能服務(wù)器為例,當(dāng)處理器在執(zhí)行任務(wù)時(shí),若所需數(shù)據(jù)存于本地節(jié)點(diǎn)的內(nèi)存中,便能以極快的速度獲取,仿若閃電般迅速;然而,一旦數(shù)據(jù)位于其他節(jié)點(diǎn)的內(nèi)存,就需通過(guò)高速互連通道長(zhǎng)途跋涉去調(diào)取,這期間的延遲便如同蝸牛爬行,明顯增加。有實(shí)驗(yàn)數(shù)據(jù)表明,在某些復(fù)雜的計(jì)算任務(wù)中,處理器訪問(wèn)本地內(nèi)存的耗時(shí)可能僅在幾十納秒,而訪問(wèn)遠(yuǎn)端節(jié)點(diǎn)內(nèi)存的耗時(shí)卻會(huì)飆升至數(shù)百納秒,甚至更多。這種差異在大規(guī)模數(shù)據(jù)處理、高性能計(jì)算等對(duì)內(nèi)存訪問(wèn)速度要求苛刻的場(chǎng)景中,影響可謂深遠(yuǎn)。

正因如此,諸多企業(yè)級(jí)應(yīng)用,像大型數(shù)據(jù)庫(kù)管理系統(tǒng)、科學(xué)計(jì)算軟件等,紛紛精心優(yōu)化內(nèi)存分配策略,竭力讓數(shù)據(jù)盡可能靠近使用它的處理器,以充分挖掘 NUMA 架構(gòu)的性能潛力,確保系統(tǒng)高效運(yùn)轉(zhuǎn)。每個(gè)節(jié)點(diǎn)都有自己的內(nèi)存(稱(chēng)為本地內(nèi)存),并可包含一個(gè)或多個(gè)處理器。節(jié)點(diǎn)和節(jié)點(diǎn)之間通過(guò) QPI(Intel QuickPath Interconnect)完成互聯(lián),其架構(gòu)如下圖所示:

圖片圖片

注:這里的 Core 指的是物理核,HT(Hyper-Threading,超線程)指的是邏輯核。

在 NUMA 架構(gòu)下,任意一個(gè) CPU 都可以訪問(wèn)所有節(jié)點(diǎn)的內(nèi)存,訪問(wèn)自己節(jié)點(diǎn)的本地內(nèi)存是最快的,但訪問(wèn)其他節(jié)點(diǎn)的內(nèi)存就會(huì)慢很多,這就導(dǎo)致了 CPU 訪問(wèn)內(nèi)存的速度不一致,所以叫做非一致性?xún)?nèi)存訪問(wèn)架構(gòu)。

NUMA 架構(gòu)嚴(yán)格意義上來(lái)講不屬于 SMP 的范疇,但是由于其每個(gè)處理器訪問(wèn)內(nèi)存的模式是一致的,所以在邏輯上屬于對(duì)稱(chēng)多處理 (SMP) 架構(gòu)的擴(kuò)展。

對(duì)稱(chēng)多處理器(SMP)

對(duì)稱(chēng)多處理器(SMP)架構(gòu)恰似一位秉持公平原則的協(xié)調(diào)者,在其構(gòu)建的系統(tǒng)世界里,所有處理器地位平等,共享同一物理內(nèi)存,無(wú)論訪問(wèn)內(nèi)存中的哪個(gè)地址,所消耗的時(shí)間都如同復(fù)制粘貼般完全一致。這種一致性使得內(nèi)存管理在某些場(chǎng)景下顯得簡(jiǎn)潔明了,易于掌控。就拿常見(jiàn)的小型服務(wù)器或工作站來(lái)說(shuō),它們處理的任務(wù)相對(duì)單一,負(fù)載較輕,SMP 架構(gòu)便能輕松應(yīng)對(duì),充分發(fā)揮資源共享的優(yōu)勢(shì),讓系統(tǒng)流暢運(yùn)行。

不過(guò),SMP 架構(gòu)也并非完美無(wú)缺,隨著處理器數(shù)量的逐步增加,它的短板開(kāi)始顯現(xiàn)。由于所有處理器都緊緊依賴(lài)同一條內(nèi)存總線去訪問(wèn)內(nèi)存,如同眾人爭(zhēng)搶獨(dú)木橋,內(nèi)存訪問(wèn)沖突愈發(fā)激烈,導(dǎo)致內(nèi)存帶寬迅速成為系統(tǒng)性能提升的瓶頸,限制了系統(tǒng)的進(jìn)一步擴(kuò)展。在內(nèi)核初始化階段,SMP 架構(gòu)也展現(xiàn)出獨(dú)特的一面,0 號(hào)處理器勇挑重?fù)?dān),擔(dān)當(dāng)引導(dǎo)處理器,負(fù)責(zé)完成內(nèi)核的初始化工作,而其他處理器則如同乖巧的學(xué)生,靜靜等待初始化完成,之后才一同參與系統(tǒng)的運(yùn)行,攜手并肩處理各項(xiàng)任務(wù)。

混合體系結(jié)構(gòu)

在現(xiàn)實(shí)復(fù)雜多變的應(yīng)用場(chǎng)景中,一種融合的智慧應(yīng)運(yùn)而生 —— 混合體系結(jié)構(gòu)。它巧妙地將 NUMA 與 SMP 的優(yōu)勢(shì)合二為一,恰似一位博采眾長(zhǎng)的智者,根據(jù)不同的應(yīng)用需求靈活調(diào)配資源,實(shí)現(xiàn)性能的優(yōu)化升華。比如,在大型數(shù)據(jù)中心的服務(wù)器集群里,整體架構(gòu)采用 NUMA 架構(gòu),充分利用其擴(kuò)展性強(qiáng)、內(nèi)存訪問(wèn)局部性好的優(yōu)勢(shì),應(yīng)對(duì)海量數(shù)據(jù)的存儲(chǔ)與處理挑戰(zhàn);

而在每個(gè) NUMA 節(jié)點(diǎn)內(nèi)部,則引入 SMP 架構(gòu),讓節(jié)點(diǎn)內(nèi)的多個(gè)處理器能夠平等、高效地共享本地內(nèi)存,協(xié)同處理任務(wù),進(jìn)一步提升執(zhí)行效率。再如,一些對(duì)實(shí)時(shí)性要求極高的工業(yè)控制系統(tǒng),通過(guò)精細(xì)的配置,使關(guān)鍵任務(wù)在 SMP 模式下的處理器上緊密運(yùn)行,確保響應(yīng)的及時(shí)性;同時(shí),將非關(guān)鍵任務(wù)合理分配至 NUMA 架構(gòu)的其他節(jié)點(diǎn),實(shí)現(xiàn)資源的優(yōu)化利用,保障整個(gè)系統(tǒng)的穩(wěn)定與高效。這種混合體系結(jié)構(gòu),憑借其靈活多變的特性,宛如一把萬(wàn)能鑰匙,能夠解鎖各種復(fù)雜應(yīng)用場(chǎng)景下的性能密碼,為系統(tǒng)的高效運(yùn)行保駕護(hù)航。

三、內(nèi)存模型:應(yīng)對(duì)復(fù)雜的物理內(nèi)存

3.1平坦內(nèi)存(Flat Memory)

在計(jì)算機(jī)系統(tǒng)的世界里,平坦內(nèi)存(Flat Memory)模型宛如一位簡(jiǎn)潔高效的組織者,適用于那些物理內(nèi)存連續(xù)或近乎連續(xù)的非 NUMA 系統(tǒng)場(chǎng)景。想象一下,在一些小型的嵌入式設(shè)備或者早期相對(duì)簡(jiǎn)單的個(gè)人計(jì)算機(jī)系統(tǒng)中,它們的物理內(nèi)存布局規(guī)整,沒(méi)有過(guò)多復(fù)雜的 “縫隙” 與 “空洞”,F(xiàn)lat Memory 模型便能大顯身手。

在這種模型下,內(nèi)核運(yùn)用一個(gè)全局的 mem_map 數(shù)組來(lái)精心管理整個(gè)物理內(nèi)存,如同一位嚴(yán)謹(jǐn)?shù)膱D書(shū)管理員,將每一頁(yè)物理內(nèi)存都按照順序在 mem_map 數(shù)組中安排得井井有條,數(shù)組的下標(biāo)即為對(duì)應(yīng)的物理頁(yè)框號(hào)(PFN)。舉例來(lái)說(shuō),若系統(tǒng)的物理內(nèi)存從地址 0 開(kāi)始,依次遞增,毫無(wú)間斷,那么 PFN 為 0 的物理頁(yè),恰好對(duì)應(yīng) mem_map 數(shù)組的第 0 個(gè)元素,以此類(lèi)推,這種一一對(duì)應(yīng)的線性關(guān)系使得內(nèi)存管理變得直觀易懂。

不過(guò),現(xiàn)實(shí)中的系統(tǒng)架構(gòu)偶爾會(huì)存在一些特殊情況,即便內(nèi)存整體連續(xù),但某些區(qū)域可能因硬件設(shè)計(jì)、預(yù)留等原因無(wú)法被正常使用,這些區(qū)域就如同圖書(shū)館書(shū)架上的 “空位”,雖占據(jù)位置卻不存放實(shí)際書(shū)籍。在 mem_map 數(shù)組中,它們同樣擁有對(duì)應(yīng)的條目,只不過(guò)與之對(duì)應(yīng)的 struct page 對(duì)象如同尚未書(shū)寫(xiě)內(nèi)容的空白紙張,不會(huì)被完全初始化,處于一種特殊的待命狀態(tài)。

當(dāng)系統(tǒng)需要分配內(nèi)存時(shí),只需基于 mem_map 數(shù)組進(jìn)行簡(jiǎn)單的偏移計(jì)算,就能迅速定位到可用的物理頁(yè),就像讀者在圖書(shū)館中根據(jù)書(shū)架編號(hào)快速找到所需書(shū)籍一般,高效便捷。

在這種模型下,處理器將物理內(nèi)存看做是一個(gè)連續(xù)的,沒(méi)有空洞的地址空間。內(nèi)核定義了一個(gè)全局的struct page數(shù)組mem_map,用于保存所有的struct page對(duì)象。由于struct page對(duì)象和 PFN 是一一對(duì)應(yīng)的,所以每個(gè) PFN 對(duì)應(yīng)著mem_map中的一個(gè)成員。

⑴全局變量 mem_map

mem_map 是一個(gè)全局變量,表示 struct page 數(shù)組,其聲明如下:

// file: mm/memory.c
 #ifndef CONFIG_NEED_MULTIPLE_NODES
 ......
 struct page *mem_map;
 ......
 #endif

mem_map 在 alloc_node_mem_map 函數(shù)中被初始化。在 NUMA 架構(gòu)下,每個(gè)節(jié)點(diǎn)由 struct pglist_data 結(jié)構(gòu)體表示,其中 pglist_data->node_mem_map 字段指示該節(jié)點(diǎn)對(duì)應(yīng)的 struct page 數(shù)組的起始位置。

// file: include/linux/mmzone.h
 typedef struct pglist_data {
 ......
 #ifdef CONFIG_FLAT_NODE_MEM_MAP /* means !SPARSEMEM */
     struct page *node_mem_map;
 ......
 #endif
 } pg_data_t;

由于平坦內(nèi)存模型相當(dāng)于只有一個(gè)節(jié)點(diǎn),所以 mem_map 對(duì)應(yīng)于節(jié)點(diǎn) 0 的 pglist_data->node_mem_map 的值。

// file: mm/page_alloc.c
 static void __init_refok alloc_node_mem_map(struct pglist_data *pgdat)
 {
     /* Skip empty nodes */
     /*
      * pgdat->node_spanned_pages 指示節(jié)點(diǎn)中的物理頁(yè)數(shù)量(包括內(nèi)存空洞)
      * 如果該值為 0,說(shuō)明該節(jié)點(diǎn)沒(méi)有內(nèi)存,直接跳過(guò)
      */
     if (!pgdat->node_spanned_pages)
         return;
 /*
  * 配置選項(xiàng) CONFIG_FLAT_NODE_MEM_MAP 指示將每個(gè)節(jié)點(diǎn)的內(nèi)存當(dāng)做平坦模型來(lái)看待
  * 對(duì)應(yīng)著非稀疏內(nèi)存模型
  * config FLAT_NODE_MEM_MAP
  *  def_bool y
  *  depends on !SPARSEMEM
  */
 #ifdef CONFIG_FLAT_NODE_MEM_MAP
     /* ia64 gets its own node_mem_map, before this, without bootmem */
     /*
      * pgdat->node_mem_map 指示節(jié)點(diǎn)對(duì)應(yīng)的 struct page 數(shù)組的起始地址
      * 如果該值為 NULL,說(shuō)明還未進(jìn)行初始化,那么就需要對(duì)其進(jìn)行初始化
      */
     if (!pgdat->node_mem_map) {
         unsigned long size, start, end;
         struct page *map;
 
         /*
          * The zone's endpoints aren't required to be MAX_ORDER
          * aligned but the node_mem_map endpoints must be in order
          * for the buddy allocator to function correctly.
          */
         /*
          * MAX_ORDER 指示伙伴系統(tǒng)中的最大分配階,擴(kuò)展為 11,
          * 表示伙伴系統(tǒng)支持 2 的 0 次方到 2 的 10 次方共 11 種內(nèi)存分配大小
          * #define MAX_ORDER 11
          * MAX_ORDER_NR_PAGES 指示最大分配階下每次分配的物理頁(yè)數(shù)量,擴(kuò)展為 1 << 10 即 1024 個(gè)頁(yè),對(duì)應(yīng) 4MB 的內(nèi)存
          * #define MAX_ORDER_NR_PAGES (1 << (MAX_ORDER - 1))
          * 
          * pgdat->node_start_pfn 表示節(jié)點(diǎn)的起始頁(yè)幀號(hào)
          * pgdat_end_pfn(pgdat) 獲取節(jié)點(diǎn)的結(jié)束頁(yè)幀號(hào)(包括內(nèi)存空洞)
          * 二者都需要向上對(duì)齊到 MAX_ORDER,這是伙伴系統(tǒng)的要求
          * size 計(jì)算出節(jié)點(diǎn)對(duì)應(yīng)的 struct page 數(shù)組占用的空間
          * 接下來(lái),從節(jié)點(diǎn)內(nèi)存中分配 size 大小的內(nèi)存用于保存 struct page 數(shù)組,內(nèi)存的起始地址保存到變量 map 中
          * 最后,根據(jù)對(duì)齊結(jié)果,修正 map 的值,并賦值給 pgdat->node_mem_map,用作 struct page 數(shù)組的起始地址
          *
          */
         start = pgdat->node_start_pfn & ~(MAX_ORDER_NR_PAGES - 1);
         end = pgdat_end_pfn(pgdat);
         end = ALIGN(end, MAX_ORDER_NR_PAGES);
         size =  (end - start) * sizeof(struct page);
         map = alloc_remap(pgdat->node_id, size);
         if (!map)
             map = alloc_bootmem_node_nopanic(pgdat, size);
         pgdat->node_mem_map = map + (pgdat->node_start_pfn - start);
     }
     
 /*
  * 內(nèi)核配置選項(xiàng) CONFIG_NEED_MULTIPLE_NODES 表示是否需要多個(gè)節(jié)點(diǎn),默認(rèn)為 yes
  * 配置了該選項(xiàng)意味著是非連續(xù)內(nèi)存模型(DISCONTIGMEM)或者 NUMA 架構(gòu)
  ************************************************************
  * config NEED_MULTIPLE_NODES
  *  def_bool y
  *  depends on DISCONTIGMEM || NUMA
  ************************************************************
  * 如果沒(méi)有設(shè)置該選項(xiàng),說(shuō)明只有一個(gè)節(jié)點(diǎn),對(duì)應(yīng)于平坦內(nèi)存模型
  */
 #ifndef CONFIG_NEED_MULTIPLE_NODES
     /*
      * With no DISCONTIG, the global mem_map is just set as node 0's
      */
     /*
      * 對(duì)于平坦內(nèi)存模型,將 mem_map 設(shè)置為節(jié)點(diǎn) 0 的 node_mem_map 的值
      * 宏 NODE_DATA 用于獲取指定節(jié)點(diǎn)的 struct pglist_data 實(shí)例
      */
     if (pgdat == NODE_DATA(0)) {
         mem_map = NODE_DATA(0)->node_mem_map;
 /*
  * 內(nèi)核配置選項(xiàng) CONFIG_HAVE_MEMBLOCK_NODE_MAP 指示對(duì)于 memblock 內(nèi)存塊是否需要區(qū)分不同的節(jié)點(diǎn),
  * memblock 用于啟動(dòng)時(shí)內(nèi)存管理
  * 
  * x86 架構(gòu)下,該配置項(xiàng)默認(rèn)為 yes
  * 此時(shí),當(dāng) mem_map 對(duì)應(yīng)的頁(yè)幀號(hào)不等于節(jié)點(diǎn)的起始頁(yè)幀號(hào)時(shí),還需要進(jìn)一步調(diào)整
  */      
 #ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
         if (page_to_pfn(mem_map) != pgdat->node_start_pfn)
             mem_map -= (pgdat->node_start_pfn - ARCH_PFN_OFFSET);
 #endif /* CONFIG_HAVE_MEMBLOCK_NODE_MAP */
     }
 #endif
 #endif /* CONFIG_FLAT_NODE_MEM_MAP */
 }

alloc_node_mem_map 函數(shù)的完整調(diào)用流程如下:

start_kernel() -> setup_arch() -> pagetable_init() -> x86_init.paging.pagetable_init() -> native_pagetable_init() -> paging_init() -> zone_sizes_init() -> free_area_init_nodes() -> free_area_init_node() -> alloc_node_mem_map()。

⑵pfn 和 page 的相互轉(zhuǎn)換

在平坦內(nèi)存模型下,pfn 和 struct page 的轉(zhuǎn)換邏輯相對(duì)簡(jiǎn)單,如下所示:

// file: include/asm-generic/memory_model.h
 #define __pfn_to_page(pfn)  (mem_map + ((pfn) - ARCH_PFN_OFFSET))
 #define __page_to_pfn(page) ((unsigned long)((page) - mem_map) + \
                  ARCH_PFN_OFFSET)

可以看出,pfn 和 struct page 實(shí)際是線性對(duì)應(yīng)的關(guān)系,PFN - ARCH_PFN_OFFSET 就是 mem_map 數(shù)組的索引 。其中,mem_map 就是上文提到的全局?jǐn)?shù)組,ARCH_PFN_OFFSET 是與處理器架構(gòu)相關(guān)的頁(yè)幀偏移量,其定義了物理內(nèi)存起始地址不為 0 的系統(tǒng)的第一個(gè)頁(yè)幀號(hào)。對(duì)于 x86 架構(gòu)而言,ARCH_PFN_OFFSET 的值為 0。

為了保證內(nèi)容的完整性,我們來(lái)看下 ARCH_PFN_OFFSET 是如何定義的。

在設(shè)置了內(nèi)核配置選項(xiàng) CONFIG_FLATMEM 的前提下,如果沒(méi)有定義 ARCH_PFN_OFFSET,那么就會(huì)將 ARCH_PFN_OFFSET 定義為 0。

// file: include/asm-generic/memory_model.h
 #if defined(CONFIG_FLATMEM)
 
 #ifndef ARCH_PFN_OFFSET
 #define ARCH_PFN_OFFSET     (0UL)
 #endif

實(shí)際上,ARCH_PFN_OFFSET 在 include/asm-generic/page.h 文件中是有定義的,但其依賴(lài)于 PAGE_OFFSET 和 PAGE_SHIFT 的實(shí)現(xiàn)。

// file: include/asm-generic/page.h
 #ifndef ARCH_PFN_OFFSET
 #define ARCH_PFN_OFFSET     (PAGE_OFFSET >> PAGE_SHIFT)
 #endif

正如我們上文所說(shuō)的,在 4KB 頁(yè)的情況下,宏 PAGE_SHIFT 擴(kuò)展為 12;而宏 PAGE_OFFSET 依賴(lài)于內(nèi)核配置選項(xiàng) CONFIG_KERNEL_RAM_BASE_ADDRESS。由于 x86 架構(gòu)不會(huì)配置該選項(xiàng),所以 ARCH_PFN_OFFSET 最終擴(kuò)展為 0。

// file: include/asm-generic/page.h
 #ifdef CONFIG_KERNEL_RAM_BASE_ADDRESS
 #define PAGE_OFFSET     (CONFIG_KERNEL_RAM_BASE_ADDRESS)
 #else
 #define PAGE_OFFSET     (0)
 #endif

另外,在 x86 架構(gòu)下,alloc_remap 函數(shù)是一個(gè)空函數(shù),所以 mem_map 數(shù)組內(nèi)存的分配實(shí)際上是在 alloc_bootmem_node_nopanic 函數(shù)中進(jìn)行的。

// file: include/linux/bootmem.h
 static inline void *alloc_remap(int nid, unsigned long size)
 {
     return NULL;
 }

在 alloc_bootmem_node_nopanic 函數(shù)的調(diào)用鏈中,最終會(huì)調(diào)用到 __alloc_memory_core_early 函數(shù),該函通過(guò) memblock_find_in_range_node 函數(shù)分配到物理內(nèi)存后,使用宏 phys_to_virt 將物理內(nèi)存地址轉(zhuǎn)換成虛擬內(nèi)存地址。

// file: mm/nobootmem.c
 static void * __init __alloc_memory_core_early(int nid, u64 size, u64 align,
                     u64 goal, u64 limit)
 {
 ......
     addr = memblock_find_in_range_node(goal, limit, size, align, nid);
 ......
     ptr = phys_to_virt(addr);
 ......
     return ptr;
 }

而 phys_to_virt 函數(shù)實(shí)際等同于 __va 宏。

// file: arch/x86/include/asm/io.h
 static inline void *phys_to_virt(phys_addr_t address)
 {
     return __va(address);
 }

__va(x) 和 __pa(x) 這兩個(gè)宏我們?cè)谝郧暗奈恼轮幸捕啻翁岬竭^(guò),其功能就是將物理內(nèi)存地址和直接映射區(qū)的虛擬地址進(jìn)行轉(zhuǎn)換。換句話(huà)說(shuō),mem_map 數(shù)組位于虛擬地址的直接映射區(qū)。平坦內(nèi)存模型下,struct page與 物理頁(yè)對(duì)應(yīng)關(guān)系示意圖:

圖片圖片

3.2不連續(xù)內(nèi)存(Discontiguous Memory)

當(dāng)系統(tǒng)的物理內(nèi)存空間出現(xiàn)了 “裂縫”,不再是連續(xù)的整體,不連續(xù)內(nèi)存(Discontiguous Memory)模型便閃亮登場(chǎng),成為解決這類(lèi)復(fù)雜內(nèi)存布局的得力助手。在非一致內(nèi)存訪問(wèn)(NUMA)架構(gòu)的系統(tǒng)中,由于內(nèi)存被劃分成多個(gè)節(jié)點(diǎn),節(jié)點(diǎn)之間的內(nèi)存地址往往存在間斷,就好比一座城市被河流、山脈分隔成多個(gè)區(qū)域,各個(gè)區(qū)域內(nèi)部建筑緊密相連,但區(qū)域之間有明顯間隔。此時(shí),Discontiguous Memory 模型便能充分發(fā)揮優(yōu)勢(shì),對(duì)這些存在空洞的內(nèi)存空間進(jìn)行高效管理。

每個(gè)節(jié)點(diǎn)都由一個(gè) struct pglist_data 結(jié)構(gòu)體來(lái)精心打理,其內(nèi)部的 node_mem_map 成員如同指向各個(gè)區(qū)域?qū)毑氐牡貓D,指向本節(jié)點(diǎn)內(nèi)物理頁(yè)描述符數(shù)組,確保在這片不連續(xù)的內(nèi)存 “拼圖” 中,每個(gè)節(jié)點(diǎn)內(nèi)部的內(nèi)存管理依然有序。

舉例來(lái)說(shuō),在一個(gè)擁有多個(gè)處理器、且內(nèi)存分布于不同插槽的服務(wù)器系統(tǒng)里,每個(gè)插槽對(duì)應(yīng)的內(nèi)存可視為一個(gè)獨(dú)立節(jié)點(diǎn),處理器訪問(wèn)本地插槽內(nèi)存時(shí)速度較快,訪問(wèn)其他插槽內(nèi)存則相對(duì)較慢。Discontiguous Memory 模型通過(guò)對(duì)不同節(jié)點(diǎn)的細(xì)致區(qū)分,讓系統(tǒng)在面對(duì)復(fù)雜的內(nèi)存訪問(wèn)需求時(shí),能夠精準(zhǔn)調(diào)度,優(yōu)先使用本地節(jié)點(diǎn)內(nèi)存,減少跨節(jié)點(diǎn)數(shù)據(jù)傳輸帶來(lái)的延遲,如同快遞員優(yōu)先派送本地包裹,避免長(zhǎng)途轉(zhuǎn)運(yùn),提升整體效率。從 PFN 到 struct page 的轉(zhuǎn)換過(guò)程,需先依據(jù) PFN 定位到所屬節(jié)點(diǎn),再通過(guò)節(jié)點(diǎn)內(nèi)的 node_mem_map 找到對(duì)應(yīng)的 struct page,這一過(guò)程雖然相較平坦內(nèi)存模型多了一步節(jié)點(diǎn)定位,但卻為復(fù)雜內(nèi)存環(huán)境下的精準(zhǔn)管理提供了可能。

在平坦內(nèi)存模型下,處理器將物理內(nèi)存看做一段連續(xù)的地址空間。但是,物理內(nèi)存可能會(huì)存在空洞。特別是在 NUMA 架構(gòu)下,各個(gè)節(jié)點(diǎn)的物理內(nèi)存地址不再連續(xù),這樣在節(jié)點(diǎn)和節(jié)點(diǎn)之間就會(huì)出現(xiàn)較大的內(nèi)存空洞。對(duì)于大多數(shù)架構(gòu),內(nèi)存空洞在 mem_map 數(shù)組中都有對(duì)應(yīng)的 struct page 對(duì)象。

也就是說(shuō),有些 page 對(duì)象實(shí)際映射到的是內(nèi)存空洞。而映射到內(nèi)存空洞的 struct page 對(duì)象永遠(yuǎn)不會(huì)完全初始化,也無(wú)法使用,所以這些 struct page 對(duì)象所占用的空間就被白白浪費(fèi)掉了。

為了解決這個(gè)問(wèn)題,引入了非連續(xù)內(nèi)存模型。在非連續(xù)內(nèi)存模型下,系統(tǒng)將每個(gè)節(jié)點(diǎn)的內(nèi)存看做是一段單獨(dú)的地址連續(xù)的平坦內(nèi)存,然后在每個(gè)節(jié)點(diǎn)對(duì)應(yīng)的pglist_data 結(jié)構(gòu)體實(shí)例的 node_mem_map 字段中,保存著該節(jié)點(diǎn)對(duì)應(yīng)的 struct page 數(shù)組的基地址。這樣,就將一段不連續(xù)的內(nèi)存空間分割成了多個(gè)連續(xù)的內(nèi)存區(qū)間,每段區(qū)間都對(duì)應(yīng)著平坦內(nèi)存模型。

圖片圖片

在非連續(xù)內(nèi)存模型下,PFN 和 struct page 之間的轉(zhuǎn)換邏輯如下所示:

// file: include/asm-generic/memory_model.h
 #define __pfn_to_page(pfn)          \
 ({  unsigned long __pfn = (pfn);        \
     unsigned long __nid = arch_pfn_to_nid(__pfn);  \
     NODE_DATA(__nid)->node_mem_map + arch_local_page_offset(__pfn, __nid);\
 })
 
 #define __page_to_pfn(pg)                       \
 ({  const struct page *__pg = (pg);                 \
     struct pglist_data *__pgdat = NODE_DATA(page_to_nid(__pg)); \
     (unsigned long)(__pg - __pgdat->node_mem_map) +         \
      __pgdat->node_start_pfn;                   \
 })

每個(gè)節(jié)點(diǎn)的struct page 數(shù)組的起始地址由 pglist_data->node_mem_map 表示(相當(dāng)于平坦內(nèi)存模型的 mem_map ),PFN 到節(jié)點(diǎn)起始頁(yè)幀號(hào)( pglist_data->node_start_pfn )的偏移量對(duì)應(yīng)著數(shù)組pglist_data->node_mem_map 的索引值,兩者相加就能得到 PFN 對(duì)應(yīng)的 struct page 的地址。

在此之前,先要獲取到 PFN 對(duì)應(yīng)的節(jié)點(diǎn) id(通過(guò) arch_pfn_to_nid),然后通過(guò)宏 NODE_DATA 獲取到節(jié)點(diǎn) id 對(duì)應(yīng)的 struct pglist_data 實(shí)例及其 node_mem_map 字段的值。宏 arch_local_page_offset 計(jì)算指定 PFN 相對(duì)于節(jié)點(diǎn)起始頁(yè)幀的偏移量,即數(shù)組的索引。

// file: include/asm-generic/memory_model.h
 #ifndef arch_pfn_to_nid
 #define arch_pfn_to_nid(pfn)    pfn_to_nid(pfn)
 #endif
 
 #ifndef arch_local_page_offset
 #define arch_local_page_offset(pfn, nid)    \
     ((pfn) - NODE_DATA(nid)->node_start_pfn)
 #endif

注意:非連續(xù)內(nèi)存模型已經(jīng)廢棄不用了。非連續(xù)內(nèi)存模型可以看做稀疏內(nèi)存模型的一種特例,而且經(jīng)測(cè)試其負(fù)載比稀疏內(nèi)存模型還要高,所以在 x86-64 架構(gòu)下該內(nèi)存模型已經(jīng)被稀疏內(nèi)存模型所替代。

在 v5.14 之后的內(nèi)核版本中,CONFIG_DISCONTIGMEM相關(guān)的代碼已經(jīng)被移除了。

3.3稀疏內(nèi)存(Sparse Memory)

稀疏內(nèi)存(Sparse Memory)模型作為 Linux 內(nèi)存管理中的 “全能選手”,專(zhuān)為應(yīng)對(duì)那些物理內(nèi)存地址空間存在大量空洞,且需要支持內(nèi)存熱插拔等高級(jí)功能的復(fù)雜系統(tǒng)而生。在現(xiàn)代大型服務(wù)器、云計(jì)算平臺(tái)等場(chǎng)景中,隨著硬件架構(gòu)的不斷升級(jí)與業(yè)務(wù)需求的動(dòng)態(tài)變化,系統(tǒng)常常需要在運(yùn)行過(guò)程中靈活增減內(nèi)存模塊,就如同搭建積木城堡時(shí),隨時(shí)可以添加或移除積木塊。Sparse Memory 模型將物理內(nèi)存巧妙地劃分為多個(gè)區(qū)段,每個(gè)區(qū)段用 mem_section 結(jié)構(gòu)體來(lái)精細(xì)描述,如同將一片廣袤的土地劃分成多個(gè)地塊,每個(gè)地塊都有詳細(xì)的規(guī)劃圖。

區(qū)段內(nèi)包含 section_mem_map,從邏輯上講,它如同指向 struct page 陣列的 “指南針”,指引著內(nèi)核找到對(duì)應(yīng)物理頁(yè)的描述信息。在區(qū)段劃分方面,不同架構(gòu)依據(jù)自身對(duì)物理內(nèi)存支持的上限以及對(duì)內(nèi)存管理粒度的需求,通過(guò) SECTION_SIZE_BITS 和 MAX_PHYSMEM_BITS 等常量來(lái)精心定義區(qū)段大小和最大區(qū)段數(shù)。例如,在某款為大數(shù)據(jù)處理定制的服務(wù)器架構(gòu)中,區(qū)段大小設(shè)置為 2^27 字節(jié)(即 128MB),如此一來(lái),面對(duì)海量數(shù)據(jù)的存儲(chǔ)與處理,內(nèi)核能夠以區(qū)段為單位靈活調(diào)配內(nèi)存,避免小塊內(nèi)存頻繁分配回收帶來(lái)的性能損耗。對(duì)于 PFN 轉(zhuǎn)換為 struct page,Sparse Memory 模型提供了兩種精妙的方式:“classic sparse” 和 “sparse vmemmap”。

前者如同古老而可靠的地圖,在 page-flags 中巧妙編碼頁(yè)面的段號(hào),并利用 PFN 的高位信息精準(zhǔn)訪問(wèn)映射該頁(yè)框的段,在區(qū)段內(nèi),PFN 則作為指向頁(yè)數(shù)組的索引,指引內(nèi)核找到目標(biāo);后者則像是引入了智能導(dǎo)航系統(tǒng),借助虛擬映射的內(nèi)存映射優(yōu)化 pfn_to_page 和 page_to_pfn 操作,通過(guò)一個(gè)全局的 struct page *vmemmap 指針,指向虛擬連續(xù)的 struct page 對(duì)象陣列,此時(shí) PFN 搖身一變成為該數(shù)組的索引,struct page 從 vmemmap 的偏移量恰好就是該頁(yè)的 PFN,這種方式大大加速了內(nèi)存管理操作的速度,為系統(tǒng)高效運(yùn)行提供了有力保障。

隨著內(nèi)存熱插拔技術(shù)的出現(xiàn),不止節(jié)點(diǎn)間內(nèi)存地址不連續(xù),單個(gè)節(jié)點(diǎn)內(nèi)的內(nèi)存地址不連續(xù)也成了常態(tài)。這時(shí)候,再用非連續(xù)內(nèi)存模型就不合適了,于是又引入了稀疏內(nèi)存模型 SPARSEMEM。稀疏內(nèi)存模型是 Linux 中最通用的內(nèi)存模型,其實(shí)不管是平坦內(nèi)存模型還是非連續(xù)內(nèi)存模型,都可以看做是稀疏內(nèi)存模型的一種特殊狀態(tài)。

稀疏內(nèi)存模型使用 section 來(lái)管理 struct page 數(shù)組,section 替代了非連續(xù)內(nèi)存模型中的節(jié)點(diǎn)的角色。由于每個(gè) section 管理的 struct page 數(shù)量比節(jié)點(diǎn)要少的多,所以管理的粒度更細(xì),更適合大塊空洞很多的場(chǎng)景。

section 由 struct mem_section 表示,其中的 section_mem_map 字段在邏輯上是指向 struct page 數(shù)組的指針。

// file: include/linux/mmzone.h
 struct mem_section {
     unsigned long section_mem_map;
 ......
 };

類(lèi)比非連續(xù)內(nèi)存模型,稀疏內(nèi)存模型示意如下:

圖片圖片

是不是跟非連續(xù)內(nèi)存模型非常相似?當(dāng)然,上圖只是一個(gè)簡(jiǎn)單的示意,并沒(méi)有展示出 section 的組織方式。另外,在稀疏內(nèi)存模型中, struct page 在虛擬地址中的布局也分為兩種,上圖也只展示了一種。

四、三級(jí)結(jié)構(gòu):精細(xì)的內(nèi)存組織框架

4.1內(nèi)存節(jié)點(diǎn)(Node)

在 Linux 內(nèi)核的內(nèi)存管理體系中,內(nèi)存節(jié)點(diǎn)(Node)宛如一座宏偉建筑的基石,處于最頂層架構(gòu),是內(nèi)存管理的關(guān)鍵起點(diǎn)。在非一致內(nèi)存訪問(wèn)(NUMA)系統(tǒng)里,內(nèi)存節(jié)點(diǎn)的劃分猶如一場(chǎng)精心策劃的布局,依據(jù)處理器與內(nèi)存的親密程度而定。每個(gè)處理器都有自己的 “近鄰” 內(nèi)存,它們緊密協(xié)作,組成一個(gè)內(nèi)存節(jié)點(diǎn),就像一個(gè)個(gè)以處理器為核心的 “小部落”。

如此一來(lái),當(dāng)處理器處理任務(wù)時(shí),優(yōu)先訪問(wèn)本地節(jié)點(diǎn)內(nèi)存,能大幅縮短數(shù)據(jù)獲取時(shí)間,如同在自家倉(cāng)庫(kù)取物般便捷高效,極大提升系統(tǒng)性能。而在具有不連續(xù)內(nèi)存的統(tǒng)一內(nèi)存訪問(wèn)(UMA)系統(tǒng)中,內(nèi)存節(jié)點(diǎn)的界定則側(cè)重于物理地址的連續(xù)性。那些連續(xù)的內(nèi)存區(qū)域被劃分成獨(dú)立節(jié)點(diǎn),如同將一片廣袤但有間斷的土地,按照連續(xù)的地塊劃分成不同區(qū)域,各自管理。

內(nèi)存節(jié)點(diǎn)通過(guò)一個(gè)名為 pglist_data 的結(jié)構(gòu)體來(lái)精準(zhǔn)描述內(nèi)存布局,這個(gè)結(jié)構(gòu)體如同一位盡職的管家,事無(wú)巨細(xì)地記錄著內(nèi)存節(jié)點(diǎn)的各項(xiàng)信息。以一個(gè)典型的 NUMA 節(jié)點(diǎn)為例,node_zones 成員如同一個(gè)收納盒,存放著多個(gè)內(nèi)存區(qū)域(Zone)結(jié)構(gòu)體,將內(nèi)存按不同特性細(xì)分;node_zonelists 則像是一張備用路線圖,指向備用的內(nèi)存區(qū)域列表,以備不時(shí)之需;nr_zones 記錄著該節(jié)點(diǎn)內(nèi)內(nèi)存區(qū)域的數(shù)量,如同清點(diǎn)家中房間數(shù)量般清晰明了。

再看 node_mem_map 成員,它仿若指向?qū)毑氐闹羔槪赶蝽?yè)描述符數(shù)組,每個(gè)物理頁(yè)都有對(duì)應(yīng)的頁(yè)描述符,就像圖書(shū)館里每本書(shū)都有專(zhuān)屬的目錄卡片,詳細(xì)記錄著書(shū)籍信息。不過(guò),它可能并非指向數(shù)組的首元素,這背后是為了滿(mǎn)足頁(yè)分配器對(duì)內(nèi)存對(duì)齊的嚴(yán)格要求,確保內(nèi)存分配的高效與穩(wěn)定,如同建筑工人按照標(biāo)準(zhǔn)尺寸砌墻,保障墻體堅(jiān)固整齊。

4.2內(nèi)存區(qū)域(Zone)

深入到內(nèi)存節(jié)點(diǎn)內(nèi)部,我們會(huì)發(fā)現(xiàn)它被進(jìn)一步細(xì)分為多個(gè)內(nèi)存區(qū)域(Zone),就像一座城市被劃分成不同功能區(qū)。內(nèi)核精心定義了多種區(qū)域類(lèi)型,每種都有其獨(dú)特使命。ZONE_DMA,作為直接內(nèi)存訪問(wèn)(DMA)區(qū)域,宛如一條為特定設(shè)備開(kāi)辟的專(zhuān)屬通道。在一些老舊的工業(yè)標(biāo)準(zhǔn)體系結(jié)構(gòu)(ISA)設(shè)備中,由于硬件限制,它們只能在特定的低地址內(nèi)存區(qū)域(通常是 0 - 16MB)進(jìn)行 DMA 操作,這個(gè)區(qū)域就是它們的 “舞臺(tái)”,確保數(shù)據(jù)能夠順暢傳輸,避免因地址不匹配導(dǎo)致的傳輸故障。

ZONE_DMA32 則像是為 64 位系統(tǒng)中的 “特殊居民” 準(zhǔn)備的專(zhuān)屬領(lǐng)地,它適用于那些僅支持 32 位地址尋址進(jìn)行 DMA 操作的設(shè)備,為它們?cè)诘?4GB 的內(nèi)存空間里預(yù)留位置,保障設(shè)備與內(nèi)存的協(xié)同工作。ZONE_NORMAL 是內(nèi)存區(qū)域中的 “主力軍”,常規(guī)可直接映射到內(nèi)核空間的內(nèi)存大多在此區(qū)域,如同城市的中心商業(yè)區(qū),承擔(dān)著主要的內(nèi)存使用任務(wù)。

對(duì)于 ARM 處理器,雖然內(nèi)核空間與物理內(nèi)存存在映射關(guān)系,但仍需借助頁(yè)表進(jìn)行精細(xì)的地址轉(zhuǎn)換,如同通過(guò)詳細(xì)的地圖導(dǎo)航才能找到目的地;而 MIPS 處理器在這方面則相對(duì)簡(jiǎn)便,部分情況下無(wú)需復(fù)雜的頁(yè)表映射,就能快速定位內(nèi)存地址。在 32 位時(shí)代,ZONE_HIGHMEM 作為高端內(nèi)存區(qū)域,是應(yīng)對(duì)內(nèi)存尋址局限的無(wú)奈之舉。當(dāng)時(shí)內(nèi)核地址空間僅有 1GB,對(duì)于高于 896MB 的物理內(nèi)存,無(wú)法直接映射,只能將其歸入此區(qū)域,后續(xù)通過(guò)特殊映射方式來(lái)使用,如同將高處的物品通過(guò)特殊工具搬運(yùn)下來(lái)。

到了 64 位系統(tǒng),內(nèi)核虛擬地址空間得到極大拓展,如同擁有了一個(gè)巨大的倉(cāng)庫(kù),不再需要 ZONE_HIGHMEM 這個(gè) “臨時(shí)儲(chǔ)物間”,所有內(nèi)存都能得到妥善安置。此外,還有 ZONE_MOVABLE 這個(gè)特殊的 “機(jī)動(dòng)部隊(duì)”,它像是內(nèi)存管理中的 “潤(rùn)滑劑”,通過(guò)靈活遷移頁(yè)面,有效防止內(nèi)存碎片的產(chǎn)生,保障內(nèi)存分配的連續(xù)性;ZONE_DEVICE 則專(zhuān)為支持持久內(nèi)存熱插拔而設(shè)立,為設(shè)備驅(qū)動(dòng)與內(nèi)存的交互提供穩(wěn)定接口,確保設(shè)備在熱插拔過(guò)程中內(nèi)存使用的穩(wěn)定性。

4.3頁(yè)(Page)

在 Linux 內(nèi)核眼中,物理內(nèi)存是以頁(yè)(Page)為基本單位進(jìn)行精細(xì)管理的,頁(yè)就如同建筑中的磚塊,是構(gòu)成內(nèi)存大廈的基礎(chǔ)單元。每一個(gè)物理頁(yè)面對(duì)應(yīng)著一個(gè) struct page 結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體如同一位忠誠(chéng)的衛(wèi)士,時(shí)刻跟蹤著頁(yè)面的使用情況。在處理器內(nèi)部,有一個(gè)神奇的 MMU(內(nèi)存管理單元)硬件,它如同一位翻譯官,負(fù)責(zé)處理虛擬內(nèi)存到物理內(nèi)存的映射,將程序使用的虛擬地址轉(zhuǎn)換為實(shí)際的物理地址,而頁(yè)表就是它的翻譯 “詞典”。

在常見(jiàn)的系統(tǒng)中,頁(yè)的大小通常為 4KB,這是綜合考慮內(nèi)存利用效率、硬件尋址能力等多方面因素后的權(quán)衡結(jié)果。就像將一塊大的內(nèi)存 “蛋糕” 切成大小合適的 “小塊”,方便分配與管理。以一個(gè)運(yùn)行多個(gè)應(yīng)用程序的系統(tǒng)為例,當(dāng)程序請(qǐng)求內(nèi)存時(shí),內(nèi)核會(huì)以頁(yè)為單位進(jìn)行分配,若程序需要 8KB 內(nèi)存,內(nèi)核會(huì)分配兩個(gè)連續(xù)的 4KB 頁(yè)面,確保內(nèi)存分配的規(guī)整與高效。

同時(shí),頁(yè)框(Page Frame)作為物理內(nèi)存的存儲(chǔ)單元,與頁(yè)一一對(duì)應(yīng),頁(yè)框號(hào)(PFN)則像每個(gè)頁(yè)框的 “身份證號(hào)”,唯一標(biāo)識(shí)每個(gè)頁(yè)框。通過(guò) PFN,內(nèi)核能夠快速定位到對(duì)應(yīng)的物理頁(yè),如同通過(guò)身份證號(hào)查找人員信息般迅速準(zhǔn)確。在內(nèi)存分配回收、頁(yè)面置換等一系列內(nèi)存管理操作中,struct page 結(jié)構(gòu)體記錄著頁(yè)面的狀態(tài)信息,如是否空閑、是否被鎖定、屬于哪個(gè)進(jìn)程等,為內(nèi)核的決策提供關(guān)鍵依據(jù),保障系統(tǒng)內(nèi)存的合理利用與穩(wěn)定運(yùn)行。

五、實(shí)戰(zhàn)案例:物理內(nèi)存模型的應(yīng)用展現(xiàn)

在科學(xué)計(jì)算領(lǐng)域,物理內(nèi)存模型的優(yōu)勢(shì)盡顯無(wú)遺。就拿大規(guī)模的天體物理模擬運(yùn)算來(lái)說(shuō),科學(xué)家們需要處理海量的數(shù)據(jù),這些數(shù)據(jù)如同浩瀚宇宙中的繁星,數(shù)量極其龐大。在采用 NUMA 架構(gòu)的超級(jí)計(jì)算機(jī)上運(yùn)行模擬程序時(shí),物理內(nèi)存模型充分發(fā)揮作用。它將內(nèi)存劃分為多個(gè)節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)緊密關(guān)聯(lián)著一組處理器。

模擬程序在運(yùn)行過(guò)程中,那些頻繁交互的數(shù)據(jù)被巧妙地分配到各個(gè)處理器對(duì)應(yīng)的本地內(nèi)存節(jié)點(diǎn)中。就像一個(gè)分工明確的科研團(tuán)隊(duì),每個(gè)成員專(zhuān)注處理自己手頭與本地資源緊密相關(guān)的數(shù)據(jù),大幅減少了數(shù)據(jù)跨節(jié)點(diǎn)傳輸?shù)难舆t。原本可能需要耗費(fèi)數(shù)小時(shí)甚至數(shù)天的模擬計(jì)算,借助物理內(nèi)存模型對(duì)內(nèi)存的精細(xì)管理,運(yùn)算時(shí)間大幅縮短,讓科學(xué)家們能夠更快地探索宇宙的奧秘,推動(dòng)科學(xué)研究的步伐。

再看云計(jì)算環(huán)境,眾多用戶(hù)的虛擬機(jī)如同一個(gè)個(gè) “虛擬房客”,共享著物理服務(wù)器的資源。這里的物理內(nèi)存模型就像一位智慧的房東,要合理分配內(nèi)存,滿(mǎn)足不同 “房客” 的需求。當(dāng)某個(gè)時(shí)間段內(nèi),部分虛擬機(jī)運(yùn)行著對(duì)內(nèi)存需求較大的企業(yè)級(jí)應(yīng)用,如大型數(shù)據(jù)庫(kù)管理系統(tǒng)、電商平臺(tái)的后臺(tái)服務(wù)等,物理內(nèi)存模型會(huì)依據(jù)系統(tǒng)負(fù)載情況,動(dòng)態(tài)調(diào)整內(nèi)存分配。

對(duì)于那些處理關(guān)鍵業(yè)務(wù)、實(shí)時(shí)性要求高的虛擬機(jī),優(yōu)先保障其內(nèi)存需求,將內(nèi)存資源從相對(duì)空閑的區(qū)域調(diào)配過(guò)來(lái),確保它們運(yùn)行流暢,避免因內(nèi)存不足導(dǎo)致的卡頓或服務(wù)中斷。這就如同在繁忙的酒店里,優(yōu)先滿(mǎn)足重要客人的房間需求,合理安排空房資源,讓整個(gè)酒店的運(yùn)營(yíng)有條不紊,為云計(jì)算用戶(hù)提供穩(wěn)定可靠的服務(wù)體驗(yàn)。

在嵌入式系統(tǒng)領(lǐng)域,以智能汽車(chē)的車(chē)載控制系統(tǒng)為例,物理內(nèi)存模型同樣發(fā)揮著關(guān)鍵作用。智能汽車(chē)內(nèi)部有眾多的電子控制單元(ECU),像發(fā)動(dòng)機(jī)控制、自動(dòng)駕駛輔助、車(chē)載娛樂(lè)等系統(tǒng),它們都運(yùn)行在有限的嵌入式芯片上。這些芯片采用的物理內(nèi)存模型針對(duì)嵌入式系統(tǒng)的特點(diǎn)進(jìn)行優(yōu)化,在內(nèi)存布局上,將與車(chē)輛安全、實(shí)時(shí)操控緊密相關(guān)的功能模塊,如剎車(chē)控制、轉(zhuǎn)向助力等,對(duì)應(yīng)的內(nèi)存區(qū)域設(shè)置為高優(yōu)先級(jí),確保數(shù)據(jù)讀寫(xiě)的及時(shí)性。

同時(shí),對(duì)于車(chē)載娛樂(lè)等非關(guān)鍵系統(tǒng),在內(nèi)存使用緊張時(shí),適當(dāng)限制其內(nèi)存占用,優(yōu)先保障行車(chē)安全相關(guān)功能的穩(wěn)定運(yùn)行。這種精準(zhǔn)的內(nèi)存管理,就像一位經(jīng)驗(yàn)豐富的駕駛員,在復(fù)雜路況下合理分配精力,確保車(chē)輛安全平穩(wěn)地行駛在道路上,為駕乘人員保駕護(hù)航。

六、回顧總結(jié)

回顧 Linux Kernel 物理內(nèi)存模型,從多樣的體系架構(gòu),到應(yīng)對(duì)復(fù)雜物理內(nèi)存的不同模型,再到精細(xì)的三級(jí)結(jié)構(gòu),每一處設(shè)計(jì)都凝聚著開(kāi)發(fā)者的智慧,旨在應(yīng)對(duì)不同硬件平臺(tái)與應(yīng)用場(chǎng)景的挑戰(zhàn),保障系統(tǒng)高效穩(wěn)定運(yùn)行。如今,隨著技術(shù)浪潮滾滾向前,內(nèi)存模型也在持續(xù)演進(jìn)。

硬件層面,新型非易失性?xún)?nèi)存不斷涌現(xiàn),如 3D XPoint 技術(shù)帶來(lái)的傲騰內(nèi)存,兼具高速讀寫(xiě)與持久存儲(chǔ)特性,促使內(nèi)核調(diào)整內(nèi)存管理策略,以充分釋放其潛能;軟件應(yīng)用領(lǐng)域,容器化技術(shù)、實(shí)時(shí)系統(tǒng)對(duì)內(nèi)存隔離性、確定性提出更高要求,推動(dòng)內(nèi)存模型優(yōu)化改進(jìn)。展望未來(lái),Linux Kernel 物理內(nèi)存模型必將在創(chuàng)新與需求的雙重驅(qū)動(dòng)下,持續(xù)進(jìn)化,為系統(tǒng)性能提升、功能拓展筑牢根基。希望本文能成為您探索 Linux 內(nèi)核的得力助手,激發(fā)您深入鉆研內(nèi)核奧秘的熱情,一同見(jiàn)證 Linux 技術(shù)生態(tài)的蓬勃發(fā)展。

責(zé)任編輯:武曉燕 來(lái)源: 深度Linux
相關(guān)推薦

2018-05-18 09:07:43

Linux內(nèi)核內(nèi)存

2013-10-11 17:32:18

Linux運(yùn)維內(nèi)存管理

2019-12-26 08:45:46

Linux虛擬內(nèi)存

2025-01-02 11:06:22

2018-12-06 10:22:54

Linux內(nèi)核內(nèi)存

2018-03-01 16:25:52

Linux內(nèi)核內(nèi)存管理

2020-07-07 07:57:39

Linux內(nèi)存碎片化

2009-10-19 09:45:06

linux內(nèi)存內(nèi)存管理

2023-10-18 13:31:00

Linux內(nèi)存

2025-03-21 00:00:00

2018-12-06 10:40:50

磁盤(pán)緩存內(nèi)存

2025-04-07 04:20:00

Linux操作系統(tǒng)內(nèi)存管理

2024-03-15 08:54:59

Linux內(nèi)核NUMA

2013-10-12 13:01:51

Linux運(yùn)維內(nèi)存管理

2017-07-25 15:09:48

Linux地址轉(zhuǎn)化

2022-08-08 08:31:00

Linux內(nèi)存管理

2022-11-21 09:09:08

Linux物理內(nèi)存管理

2017-05-18 16:30:29

Linux內(nèi)存管理

2021-11-05 15:00:33

鴻蒙HarmonyOS應(yīng)用

2021-11-08 15:06:15

鴻蒙HarmonyOS應(yīng)用
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)