Linux核心剖析 核心的記憶體管理

2021-09-25 16:00:48 字數 2514 閱讀 5436

核心的記憶體管理不同於使用者空間的記憶體管理,首先來講核心本身的限制點就比較多,比如核心一般不能睡眠,因此處理記憶體錯誤一般來講是件很困難的事情,再加上其他限制以及核心記憶體機制不能太複雜導致想獲取核心記憶體變成了一件難事。

在說核心的記憶體管理機制之前,首先我們得明白核心記憶體管理的基本單位才行,一般來講核心都是以頁為單位對核心進行記憶體管理的,正因為如此mmu(管理記憶體並把虛擬位址轉換為實體地址的硬體)以頁為單位來管理系統中的頁表,從虛擬記憶體的角度來看,頁就是最小單位。

核心中一般用struct page這個結構體來表示系統中的每個物理頁,

這裡我只說其中兩個域的作用,_count域,相當於頁的引用計數,當改計數不為0時則證明該頁被引用了多少次,當其值為0時說明當前核心並未使用改頁,該頁就變為了空閒頁。virtual域,這是個很有意思個人感覺也很重要的域,因為它就是在虛擬記憶體中該頁的位址,換句話說就是記錄了對映關係的東西。但在有些情況下有些記憶體並不永久的對映到核心的位址空間上,這個域的值為null,需要的時候必須動態對映的這些頁。這指的就是核心中的高階記憶體。

核心系統正是靠著這樣的乙個結構體去管理者核心記憶體的,通過這個結構系統可以知道那個頁被分配了,以及擁有者是誰,擁有者可能是使用者空間程序、動態分配的核心資料、靜態核心**、頁快取記憶體等(這個是重點***xx)。

分割槽,這個核心記憶體管理的另一種機制。那已經有頁管理方式了為什麼要分割槽呢?因為在核心中有些頁位於特定的實體地址上,具有侷限性。所以核心就不得不把頁分為不同的區。因為linux上存在兩種由於硬體存在的缺陷引起的記憶體定址。

1.一些硬體只能用某些特定的記憶體來執行dma(直接記憶體訪問)

2.一些體系結構其記憶體的物理定址範圍比虛擬記憶體定址範圍大的多,這樣就有一些虛擬記憶體不能永久地對映到核心空間上。

因此linux使用了三種區:

zone_dma——這個區包含的頁能用來執行dma操作

zone_normal——這個區包含的都是能正常對映操作

zone_highmem——這個區包含「高階記憶體」,其中的頁並不能永久對映到核心位址空間。

這樣設計的主要原因是為了適應不同的體系結構而已,因為不同的機器支援的機制都是不一樣的。要明確一點,區的劃分是邏輯劃分,若某個區的記憶體不夠了,是可以從其他區拿的。這樣做分割槽是為了更好的適應不同的機器但並不是絕對的。

既然已經知道了核心記憶體的基本存在的形式了,那麼就應該了解下核心記憶體的獲取了。

我想大多人都知道malloc()函式,那麼kmalloc()和vmalloc()其實和它差不多,都屬於malloc一族的函式,但是後兩者屬於系統呼叫(簡單來講,就是系統自帶的函式,在核心中使用的),而malloc屬於庫函式(簡單來講,就是使用者空間使用的函式),雖然他們三個都是用來獲取記憶體的,但是是有著本質區別的。

kmalloc()分配核心記憶體空間,其分配的記憶體空間是保證物理記憶體上是連續的,物理記憶體上是連續的那麼邏輯記憶體上當然也是連續的。

vmalloc()分配核心記憶體空間,其分配的記憶體空間是保證邏輯記憶體上是連續的,物理記憶體不一定連續,後續通過「修正」頁表保證對映到邏輯位址空間的連續區域中。

malloc()分配使用者態的記憶體,其分配的記憶體同vmalloc()相似,保證虛擬位址空間是連續的。

釋放的時候其都有對應的釋放函式,kfree(),vfree(),free()。

需要注意的是,在核心中一般使用的都是kmalloc(),因為效能上來講vmalloc()要差很多,因為要把不連續的物理記憶體轉為連續的虛擬記憶體就必須要去建立頁表,而且還需要去把頁拿著乙個乙個的去進行對映,這會造成tlb(一種硬體緩衝區,很多體系結構用它來快取虛擬位址到實體地址的對映關係,極大的提公升了系統效能)抖動,所以vmalloc()不得已才使用。

重點來了,對於核心來講這是個很重要的機制。分配和釋放資料結構是所有核心中最普遍的操作之一。為了變為資料的頻繁分配和**,程式設計者經常會用到乙個空閒鍊錶,該空閒鍊錶包含有可提供使用的、已經分配好的資料結構塊。這樣就放便了結構的分配與**。

但是在核心中,空閒鍊錶是不能全域性控制的,當記憶體緊缺時,核心無法通知空閒鍊錶讓其騰出空間來,事實上核心根本都不知道有這個東西,所以linux就設計了slab層,用來幫助結構的分配和**。

slab層的設計

slab把不同的物件劃分為所謂的快取記憶體(cache)組,其中每個快取記憶體都存放在不同型別的物件,每種物件型別對應乙個快取記憶體。例如乙個快取記憶體用於存放task_struct,乙個用來存放struct inode。

然後把這些快取記憶體又劃分為slab,slab由乙個或多個物理上連續的頁組成,一般情況下slab頁就僅僅由乙個頁組成,每個快取記憶體可以由多個slab組成。

每個slab都包含了一些物件,物件指的是被快取的資料結構,每個slab都由三種狀態,滿,空,部分滿。怎麼理解呢?就是你需要該結構時,從乙個部分滿的slab中拿,若沒有部分滿就從空的slab中拿,該slab就被標記為部分滿,當你把其中的結構拿完時,該slab就被標記為空,釋放的時候就歸還該slab。這種策略可以對比空閒鍊錶可以有效的減少記憶體碎片。

Linux 記憶體管理之核心態剖析

記憶體管理不僅是 linux 系統中比較難理解的模組,更是網上資料講解混亂的模組。本場 chat 旨在讓讀者了解記憶體分配的來龍去脈,從硬體原理和核心角度出發,分析如下內容 cpu 通過 mmu 訪問實體地址的原理 記憶體的 zone 劃分 dma normal 和 highmem 記憶體的 pag...

linux核心原理剖析 記憶體定址(一)

最近總想分享點硬核的原創文章出來,一是硬核技術是乙個程式設計師真正應該修煉 的內功 二是修煉硬核技能是通往架構師領域的必經之路。本系列文章將分享關 於linux核心設計原理相關的內容,希望能打通我們的七經八脈,真正領悟底層系 所謂記憶體定址,簡單說來就是cpu接受到指令後需要從記憶體中取得相應資料,...

linux核心剖析之main c

main函式主要做一些初始化,比如記憶體,塊裝置 字元裝置等的初始化,然後建立子程序開啟bin sh mem init main memory start,memory end trap init 陷阱門 硬體中斷向量 初始化。kernel traps.c blk dev init 塊裝置初始化。k...