linux內存管理機制

一、存管理單元MMU

高性能處理器一般都會提供一個內存管理單元MMU,該單元輔助操作系統進行內存管理,提供虛擬地址和物理地址的映射、內存訪問權限保護和Cache緩存控制等硬件支持。

TLB:Translation Lookaside Buffer 轉換旁路緩存,是MMU的核心部件,它緩存少量的虛擬地址與物理地址的轉換關係,是轉換表Cache,也稱爲“快表”。

TTW:Translation Table walk 轉換漫遊,當TLB中沒有緩存對應的地址轉換關係時,需要通過對內存轉換表的訪問來獲得虛擬地址與物理地址的對應關係。TTW成功後,寫入TLB 。

MMU具有虛擬地址與物理地址轉換,內存訪問權限保護等功能,這使得linux操作系統能單獨爲系統的每個進程分配獨立的內存空間並保證用戶空間不能訪問內核空間的地址,爲操作系統的虛擬內存管理模塊提供硬件基礎。

二、linux內存分配

對有MMU的處理器而言,Linux系統的進程所能訪問的內存空間達到4G。4G空間分爲兩部分:用戶空間和內核空間。用戶空間一般爲0~3G,內核空間爲3~4G,用戶進程通常只能訪問到用戶空間的虛擬地址,要通過系統調用纔可以訪問到內核空間。每個進程的用戶空間都是完全獨立、互不干擾的,而內核空間是由內核映射,它不會跟着進程改變,是固定的。3~4G的內核空間中,從低到高的地址依次爲:物理內存映射區--隔離帶--vmalloc虛擬內存分配器--高端內存映射區--專用頁面映射區--保留區。

三、內存存取

1、用戶空間內存動態申請

malloc(); //用戶空間動態申請內存

free(); //內存的釋放

兩者成對出現,誰申請,就有誰釋放。

如: char *p=malloc(...);

if(p==NULL)

....;

function();

...;

free(p);

p=NULL;

對於內核而言,C庫的malloc()函數通常由brk()和mmap()兩個系統調用來實現。

2、內核空間內存動態申請

Linux內核空間內存涉及的函數主要包括kmalloc() 、__get_free_pages()和vmalloc()等。kmalloc() 、__get_free_pages()(及類似函數)申請的內存位於物理內存映射區,而且物理上也是連續的,它們和物理地址只有一個固定的偏移,因此存在簡單的轉換關係。vmalloc()在虛擬內存空間給出一塊連續的內存區,實質上映射到物理空間上是不連續的,映射的轉換關係相對複雜。

kmalloc()

函數原型:

#include <linux/slab.h>
     void *kmalloc(size_t size, int flags);

void kfree(void *);

size--- 分配的塊的大小;flags----分配標誌,用於控制Kmalloc()的行爲。kmalloc()的底層依賴__get_free_pages()實現。申請的內存由kfree()釋放。

size 參數

內核管理系統的物理內存,物理內存只能按頁面進行分配。kmalloc 和典型的用戶空間 malloc 在實際上有很大的差別,內核使用特殊的基於頁的分配技術,以最佳的方式利用系統 RAM。

必須注意的是:內核只能分配一些預定義的、固定大小的字節數組。kmalloc 能夠處理的最小內存塊是 32 或 64 字節(體系結構依賴),而內存塊大小的上限隨着體系和內核配置而變化。考慮到移植性,不應分配大於 128 KB的內存。若需多於幾個 KB的內存塊,最好使用其他方法。

flags 參數

內存分配最終總是調用 __get_free_pages 來進行實際的分配,這就是 GFP_ 前綴的由來。

所有標誌都定義在 <linux/gfp.h> ,有符號代表常常使用的標誌組合。

主要的標誌常被稱爲分配優先級,包括:

GFP_KERNEL

最常用的標誌,意思是這個分配代表運行在內核空間的進程進行。內核正常分配內存。當空閒內存較少時,可能進入休眠來等待一個頁面。當前進程休眠時,內核會採取適當的動作來獲取空閒頁。所以使用 GFP_KERNEL 來分配內存的函數必須是可重入,且不能在原子上下文中運行。

GFP_ATOMIC

內核通常會爲原子性的分配預留一些空閒頁。噹噹前進程不能被置爲睡眠時,應使用 GFP_ATOMIC,這樣kmalloc 甚至能夠使用最後一個空閒頁。如果連這最後一個空閒頁也不存在,則分配返回失敗。常用來從中斷處理和進程上下文之外的其他代碼中分配內存,從不睡眠。

GFP_USER

用來爲用戶空間分配內存頁,可能睡眠。

GFP_HIGHUSER

類似 GFP_USER,如果有高端內存,就從高端內存分配。

GFP_NOIO

GFP_NOFS

功能類似 GFP_KERNEL,但是爲內核分配內存的工作增加了限制。具有GFP_NOFS 的分配不允許執行任何文件系統調用,而 GFP_NOIO 禁止任何 I/O 初始化。它們主要用在文件系統和虛擬內存代碼。那裏允許分配休眠,但不應發生遞歸的文件系統調用。

__get_free_pages()

__get_free_pages()系列函數/宏包括 get_zeroed_page()、 __get_free_page()和__get_free_pages().這些函數/宏是linux內核本質上最底層的用於獲取空閒內存的方法。最底層的內存申請總是以頁爲單位的。

get_zeroed_page(unsigned flags);返回一個指向新頁的指針並且將該頁清零。

__get_free_page(unsigned flags);返回一個指向新頁的指針,但該頁並不清零

__get_free_pages(unsigned flags ,unsigned int order);該函數可分配多頁並返回分配內存的首地址,分配的頁數爲2^order,分配的頁也不清零。order允許的最大值爲10或11。

都調用的函數alloc_pages()函數,其可在內核空間分配,也可以在用戶空間分配。alloc_pages()函數的返回值是分配第一頁的描述符而非首地址。

分配內存的釋放:

void free_page(unsigned long adder);

void free_pages(unsigned long adder , unsigned long order);

vmalloc()

vmalloc 是一個基本的 Linux 內存分配機制,它在虛擬內存空間分配一塊連續的內存區(注意是虛擬內存空間連續,不一定物理內存連續),儘管這些頁在物理內存中不連續 (使用一個單獨的 alloc_page 調用來獲得每個頁),但內核認爲它們地址是連續的。 應當注意的是:vmalloc 在大部分情況下不推薦使用。因爲在某些體系上留給 vmalloc 的地址空間相對小,且效率不高。函數原型如下:

#include <linux/vmalloc.h>
void *vmalloc(unsigned long size);//分配
void vfree(void * addr); //釋放


kmalloc 和 _get_free_pages 返回的內存地址也是虛擬地址,其實際值仍需 MMU 處理才能轉爲物理地址(-3G)。vmalloc和它們在使用硬件上沒有不同,不同是在內核如何執行分配任務上:kmalloc 和 __get_free_pages 使用的(虛擬)地址範圍和物理內存是一對一映射的, 可能會偏移一個常量 PAGE_OFFSET 值,無需修改頁表。

而vmalloc使用的地址範圍完全是虛擬的,且每次分配都要通過適當地設置頁表來建立(虛擬)內存區域。 vmalloc 可獲得的地址在從 VMALLOC_START 到 VAMLLOC_END 的範圍中,定義在 <asm/pgtable.h> 中。vmalloc 分配的地址只在處理器的 MMU 之上纔有意義。當驅動需要真正的物理地址時,就不能使用 vmalloc。 調用 vmalloc 的正確場合是分配一個大的、只存在於軟件中的、用於緩存的內存區域時。注意:vamlloc 比 __get_free_pages 要更多開銷,因爲它必須即獲取內存又建立頁表。因此, 調用 vmalloc 來分配僅僅一頁是不值得的。vmalloc 的一個小的缺點在於它無法在原子上下文中使用。因爲它內部使用 kmalloc(GFP_KERNEL) 來獲取頁表的存儲空間,因此可能休眠。

kmem_cache_alloc()

  struct kmem_cache *kmem_cache_create(const char *name, size_t size,

  size_t align, unsigned long flags,

  void (*ctor)(void*, struct kmem_cache *, unsigned long),

  void (*dtor)(void*, struct kmem_cache *, unsigned long))

  void *kmem_cache_alloc(struct kmem_cache *c, gfp_t flags)

kmem_cache_create/ kmem_cache_alloc是基於slab分配器的一種內存分配方式,適用於反覆分配釋放同一大小內存塊的場合常見的Linux內核中內存分配 - leon - 我的奮鬥首先用kmem_cache_create創建一個高速緩存區域,然後用kmem_cache_alloc從 該高速緩存區域中獲取新的內存塊常見的Linux內核中內存分配 - leon - 我的奮鬥 kmem_cache_alloc一次能分配的最大內存由mm/slab.c文件中的MAX_OBJ_ORDER宏 定義,在默認的2.6.18內核版本中,該宏定義爲5, 於是一次最多能申請1<<5 * 4KB也就是128KB的 連續物理內存常見的Linux內核中內存分配 - leon - 我的奮鬥分析內核源碼發現,kmem_cache_create函數的size參數大於128KB時會調用BUG()常見的Linux內核中內存分配 - leon - 我的奮鬥測試結果驗證了分析結果,用kmem_cache_create分 配超過128KB的內存時使內核崩潰常見的Linux內核中內存分配 - leon - 我的奮鬥

 

發佈了1 篇原創文章 · 獲贊 0 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章