linux的記憶體管理概述

2021-06-18 12:32:36 字數 4342 閱讀 9233

今年工作這麼難找?小黃同志要努力啊········咱可不能拖社主義的後退啊·····

linux將物理記憶體分成每個4k大小的頁,來進行管理。

linux中 號稱 pfn, 是 page frame number的縮寫。 取值範圍是 0----(memory size >> 12)。

但是由於物理記憶體對映的關係,物理記憶體的0位址對應到到系統上並不是實體地址的0。 例如:s3c2440上,記憶體的位址是從0x30000000開始的。當連線ram後,ram的0位址在s3c2440看來,就是0x30000000.  所以在系統中 pfn的值 應該等於 (physical address - memory base address) >> 12 。

但是linux中,用的又都是虛擬位址,所以要先將 virtual address轉換成 physical address 才行。 所以在linux中有個巨集定義 將核心線性空間的虛擬位址和pfn轉換:

[cpp]view plain

copy

print?

#define virt_to_pfn(kaddr)  (__pa(kaddr) >> page_shift)   //__pa() 將虛擬位址轉化成實體地址  

#define pfn_to_virt(pfn)    __va((pfn) <

#define virt_to_pfn(kaddr)	(__pa(kaddr) >> page_shift)   //__pa() 將虛擬位址轉化成實體地址  

#define pfn_to_virt(pfn) __va((pfn) << page_shift)

所以pfn在linux核心中應該對應的是頁幀號。可以通過簡單的轉換找到對應的實體地址。

每個物理上的頁,核心給與之分配了乙個描述符來描述: page。 pfn可以和page進行轉換:

[cpp]view plain

copy

print?

#define page_to_pfn __page_to_pfn

#define pfn_to_page __pfn_to_page

#define page_to_pfn __page_to_pfn

#define pfn_to_page __pfn_to_page

[cpp]view plain

copy

print?

#define virt_to_page(addr)  (mem_map + (((unsigned long)(addr)-page_offset) >> page_shift))

#define page_to_virt(page)  ((((page) - mem_map) <

#define virt_to_page(addr)	(mem_map + (((unsigned long)(addr)-page_offset) >> page_shift))

#define page_to_virt(page) ((((page) - mem_map) << page_shift) + page_offset)

所有的page結構存放在mem_map中,方便進行管理。

由於linux是將物理記憶體分成4k大小的頁來進行管理的。所以在軟體上也會設定mmu進行匹配對應。後面敘述。

linux將4g的虛擬空間分成了使用者空間和核心空間。使用者空間是0---3g,核心空間是3g---4g。 從核心空間可以訪問到使用者空間,但是從使用者空間必須通過系統呼叫來能訪問核心空間。

linux的核心空間分為

為什麼要講核心空間這樣分呢?

如上所述,核心空間要可以同時訪問核心空間和使用者空間,說白了就是所有空間。 因為核心要訪問所有的空間,即所有記憶體。但是當記憶體大於1g的時候,而核心空間只有1g,那麼核心就要用一種方法,將大於1g的ram對映到自己1g的空間。使用自己1g的空間來訪問大於1g的ram。並且,核心還不能完全使用自己1g的空間來完全對映所有記憶體,因為還有一些其他空間,如:暫存器等,還有一些空間留作管理等。

所以核心要綜合考慮,既要用自己的有限的空來,來訪問可能大於1g的記憶體。還有留下一寫空間來使用其他功能。 就有了上面的分類。

來分以上的兩個方面討論,1 如果使用有限的空間訪問大於1g的記憶體。 2. 剩下的空間做什麼用。

1.  linux核心將記憶體 分為 低位記憶體和高位記憶體。低位記憶體是<896m的記憶體,高位記憶體是》896m的記憶體。 

低於896m的記憶體使用線性對映,對映到 圖中的 直接對映區,可以直接訪問。在這個位置的 虛擬核心 和 物理記憶體是 線性的關係。 物理記憶體 = 虛擬記憶體 - 3g。

高位896m的記憶體使用 圖中的kmap區,kmap區只有4m,所以高位記憶體只能部分對映 到kmap區,然後訪問。 訪問結束後 取消對映,繼續對映其他的高位記憶體。當然這是有演算法的。 高位也有 臨時對映和永久對映和vmalloc的方式。當申請高位記憶體時,如alloc_page(),返回的並不是記憶體的起始線性位址,因為高位位址的起始位址可能會大於32bit,也不會在896m的區間內,所以,alloc_page返回的是高階記憶體的 page 描述符。顯然,他的  page 的描述符 會在《896m的空間內。得到描述符後,會進行再次的對映對映到 kmap區。 因為嵌入式中記憶體還很少有》896m的,這裡不做討論高位。

低位記憶體的對映: 在記憶體的啟動階段,在head.s檔案中,會在c0004000的地方建立乙個臨時的對映表。 僅僅將kernel code的空間對映到直接對映區。並執行。 在執行到後面,會在start_kernel>paging_init函式中,正式的對映。 將所有的低位記憶體 對映到 直接對映區, 也會對映中斷向量表 到ffff0000,和0位址。這樣就完成了低位記憶體的核心空間的對映。 (高位記憶體好像也會在paging_init中進行簡單的對映等,沒仔細看code)。

2. 剩下的空間做什麼?還剩下 動態對映區 和 固定對映區。

動態對映區主要是用來vmalloc用的。由於在 直接對映區是線性對映。kmalloc等 申請的空間都是對映在直接對映區,因為虛擬位址連續的,所以實體地址也是連續的。但是核心會不短malloc和不斷的free,就會造成很多不連續的位址。(當然內會也會有自己的演算法,儘量減少不連續的位址)。 所以就 找了這段空間,用vmalloc能申請出虛擬位址上連續實體地址上不連續的空間,就對映在動態對映區這段空間中。 注意:vmalloc申請的空間也可能是 在高位記憶體中。

固定對映區用來對映一些用於特殊用途的記憶體,如acpi_base等。

在程序建立的時候,核心會給他分配相應的頁表等,來讓其訪問。

由以上可知,linux對記憶體的管理是按頁管理的。但是由於在正常的使用中,有時候會 kmalloc(sizeof(struct ....));等這樣。核心這樣分配給其乙個page顯然是不合適的,浪費的。 所以有了slab/slub 模組。

slab是在2.4和2.6.**之前的乙個分配器。 slub是新版本的乙個分配器。

在建立相應的 slab時,他會預先申請一連串的 相應的struct 結構體空間。 當進行申請時候,會直接從list中找乙個free的空間 返回。這樣節省了時間,也節省了空間。

但是我們又時候還會隨意的申請malloc(10)這樣的空間。所以slab在linux啟動初始化時,會預先申請 2  4   8 16  32 64  128......等這樣位元組的空間,當要malloc(10)時候,他會自動從 16的預先得到的空間中取出乙個返回。

忽然想起: vmalloc時候,後面的size是 按page對齊的。所以當vmalloc申請小資料時候,是按照kmalloc申請的,當大資料時候,才會不連續的page分配。

pae技術:以前的32bit的晶元最多定址4g空間,但是由時候4g的記憶體是遠遠不夠的。所以intel有了pae技術(physical address extension)。將位址線從32擴充套件到了36bit,達到了記憶體64g。

節點:理論上說,cpu不管訪問記憶體中的任何位址,所需的時間應該是一樣的,但是在mips等一些結構上著個結論是不成立的。所以linux2.6支援將記憶體分成不同的節點。 cpu訪問乙個節點內的任意空間所需的時間是相等的。在arm等結構中,應該用不到這個節點。但是為了相容arm中的節點數為1.

zone:有些cpu訪問位址是由限制的,如dma的destination address必須是《32m的,所以linux 也將記憶體分成了zone_dma ,zone_normal, zone_highmem區域。 當alloc_page時,可以對區域進行選擇。

保留頁框池: != 記憶體池。

夥伴系統演算法:系統中會不斷的alloc_page,free_page,這樣會知道有很多小的記憶體塊。 linux有夥伴演算法,夥伴演算法會自動尋找和他相連的記憶體塊是不是free的,如果是,就將其和自己連線到一起成為乙個大的記憶體塊。

Linux記憶體管理概述

該圖 作者aryang 下面對各部分進行概述。linux程序的線性位址空間 程序虛擬位址空間分布 0 3g是user位址空間,3 4g是kernel位址空間。適用於arm x86等,mips按0 2g,2 3g劃分 1.緊接著核心資料區向上是mem map全域性page陣列。3.核心動態載入驅動模組...

記憶體管理 記憶體管理概述

儲存器的發展方向是高速 大容量和小體積,即儲存器嘗試更高讀寫速度,更大儲存容量,更小物理體積。在計算機中,常見的儲存器有 暫存器,快取,記憶體,硬碟,一般硬碟之類的輔助儲存器又稱外存。在平均讀寫速度上,有 暫存器 快取 記憶體 外存 在單位容量 上,有 外存 記憶體 快取 暫存器 cpu處理器只能直...

Spark 記憶體管理概述

spark被稱作記憶體計算引擎,使得很多初學者認為spark執行環境一定需要大量記憶體,更有甚者,認為spark執行期會把原始資料一次性全部載入到記憶體。解開這些疑團,需要了解spark執行時的機制,spark的計算發生在executor,因此,這裡的執行時機制是指executor的行為。對於spa...