Linux核心學習:記憶體管理
理解Linux記憶體管理能夠更好地理解程序的虛擬空間排布(當一個程序需要某塊記憶體時,它會把自己的一個記憶體地址交由OS處理)。
雖然程序只關心如何使用自己的線性排列的虛擬地址,而不需要關心實體記憶體的實際容量,以及如何使用真實的實體記憶體,但記憶體管理是核心最複雜的同時也是最重要的一部分;它是協作處理器與核心的關鍵流程,所以本文我們主要介紹Linux記憶體管理的相關知識點。
典型的程序記憶體排布如下:
作業系統有保護措施,阻止堆和棧的越界行為發生
記憶體管理
Linux把實體記憶體劃分三個層次來管理
1、儲存節點(node)
2、管理區(zone)
3、頁(page)
記憶體管理涉及的領域: * 記憶體中實體記憶體頁(記憶體管理的基本單位)的管理 * 分配大塊記憶體的夥伴系統 * 分配小塊記憶體的slab分配器 * 分配非連續記憶體塊的vmalloc機制 * 程序的地址空間
NUMA計算機
NUMA(非一致性記憶體訪問):多處理器計算機系統的各個CPU都有自己的本地記憶體;NUMA是稀疏且不連續的記憶體模型,在給定不同記憶體單元的訪問時間可能不一樣,系統的實體記憶體被劃分為幾個節點(node),在一個單獨的節點內,任一給定CPU訪問頁面所需的時間都是相同的。
分頁管理
分頁是為了實現非連續分配,以便解決記憶體碎片問題;由於程式存在區域性化特徵,這意味著在特定時間內只有部分記憶體會被頻繁訪問,具體點說,程序空間中的text段(程式碼段)、堆、棧、共享庫都是程序空間的某個特定部分,這樣會導致程序空間是非常稀疏的,於是從硬體層面開始,頁表實現採用分級 頁表
。
在說明 頁表
前,我們先介紹幾個重要概念:頁、頁框、頁幀
-
頁:本質上是資料塊,是資訊的物理單位(段才是邏輯單位),是為了更高效和更經濟的管理記憶體、線性地址被分為固定長度單位的組。
-
頁框:本質上是儲存區域,是記憶體儲存單元,也是主存的一部分(可用於任何事情:存放核心資料、快取資料、使用者資料等);分頁單元把所有的RAM分為固定長度的頁框,每個頁框包含一個頁。
-
頁幀:代表系統記憶體的最小單位,對記憶體中的每個頁都會建立頁描述符
struct page
的一個例項。頁描述符存放在mem_map中,是核心中能區分哪些頁框包含哪些程序,而哪些頁框包含的是核心程式碼或核心資料。
層次化的 頁表
是個對映表的概念,即從程序的線性地址對映到儲存器的實體地址;主要用於支援大地址空間的快速、高效管理
頁表也用於使用者程序的虛擬地址空間和系統實體記憶體(記憶體、頁幀)之間的關聯,向每一個程序提供了一致肚餓虛擬地址空間,還可以在不不額外增加實體記憶體的情況下,將頁換出到塊裝置來增加有效的可用記憶體空間。
Linux核心通過四級頁表把虛擬記憶體空間分成5部分(4個頁用於選擇頁,1個索引用於頁內偏移)
記憶體區管理
Linux因為必須處理80X86體系結構,所以在硬體上限制了頁框的使用方式,這中限制主要是由於硬體存在缺陷而引起的記憶體定址問題。為了應對這種限制,Linux 2.6把記憶體節點分為幾個區:
-
ZONE_DMA:低於16MB的頁框
-
ZONE_NORMAL:高於16MB低於896MB的頁框;大多數核心的操作只操作這個區域,系統記憶體由很多固定大小的記憶體塊組成,這樣的記憶體塊叫“頁”
-
ZONE_HIGHMEM:高階記憶體;高於896MB的框,在32位系統中,896MB邊界上的頁框不對映在核心線性地址空間的第四個GB,因此核心不能直接訪問它們,不過64位系統不存在這個問題
當核心呼叫一個記憶體分配函式時,必須指明請求頁所在的管理區
核心可以採用三種不同的機制將頁框對映到高階記憶體(ZONE_HIGHMEM)中:
-
永久核心對映(可能阻塞程序)
-
臨時核心對映(不阻塞程序)
-
非連續記憶體分配
非連續記憶體管理
把記憶體對映到一組連續的頁框是最好的選擇,這樣會利用快取記憶體並獲得較低的平均訪問時間。不過對於記憶體區的請求不是很頻繁,那麼通過連續的線性地址訪問非連續的頁框這樣的一種分配方式會很有意義。
優點:避免外碎片
缺點:打亂核心頁表
非連續記憶體管理的主要用途有三點:
-
為活動的交換區分配資料結構
-
為模組分配空間
-
給某些I/O驅動程式分配緩衝區
初始化
在啟動過程期間,儘管記憶體管理尚未初始化(初始化後,交由夥伴系統承擔),但核心仍然需要分配記憶體以建立各種資料結構,這個過程通過bootmem進行分配
bootmem作用於夥伴系統前,是在啟動階段的早期分配記憶體的,在需要分配記憶體時,它會逐位掃描點陣圖(每次分配都必須從頭掃到尾),然後每次通過對記憶體進行線性搜尋實現分配,直至找到一個能提供足夠連續頁的位置,這個過程也叫 first-best。
夥伴系統
夥伴系統演算法(fit-best)是核心為分配一組連續的 頁框
而建立的一種分配策略,這個演算法可以避免外碎片
夥伴系統的原理主要是通過一種技術紀錄現存的空閒連續頁的情況,以儘量為滿足對小塊的請求而非分隔打的空閒塊
外碎片:指系統中無法利用的小儲存塊。 內碎片:指分配給作業的儲存空間中未利用的部分
夥伴系統分配方案是一個結合2的方冪個分配器和空閒緩衝區合併計算的記憶體分配方案,它的基本思想是:把記憶體劃分為很多不同頁面規格的大塊,當向記憶體申請特定大小的塊時,會判斷是否存在該大小的塊,有則分配,無則把更大的塊分為兩部分,一部分進行分配,一部分處於空閒,用於以後的分配。當一個塊被最終釋放時,其夥伴將被檢測出來,如果夥伴也處於空閒則被合併兩者
slab
夥伴系統採用頁框作為基本記憶體區,這適合對大塊記憶體的請求,那麼小塊記憶體呢?這裡我們使用slab分配器。
slab說白了就是一種空間換時間的技術,它把物件 分組
放到 快取記憶體
中。包含快取記憶體的記憶體區被劃分為多個slab,每個slab由一個或多個連續頁框組成,這些頁框既包含分配物件,頁包含空閒物件。
slab分配器所管理的物件可以在記憶體中對齊,通常是2的倍數,這個倍數稱為 對齊因子
。對齊因子允許最大的範圍是4096,4k頁就是頁框的大小。
核心使用 map_vm_area將分散的實體記憶體頁連續對映到虛擬的vmalloc區域
提供小塊記憶體不是slab分配的唯一任務,它也作為一個快取,這個快取主要針對經常分配與釋放的物件。
使用slab有兩大好處:
一、呼叫夥伴系統的操作對作業系統的資料和指令的快取記憶體有相當的影響。更輕量的slab在可能的情況下減少了對夥伴系統的呼叫。
二、如果資料儲存在夥伴系統直接提供的頁中,那麼其地址總是出現在2的冪次整數倍附近。這對CPU快取記憶體有負面的影響,由於這種地址分佈,使得某些快取行過度使用,而某些則幾乎為空。通過slab著色,slab分配器能均勻地分佈物件,以實現均勻的快取利用。