(筆記)Linux內核學習(九)以內核內存管理方式

一 頁

       內核把物理頁做爲內存管理的基本單位;內存管理單元(MMU)把虛擬地址轉換爲物理node

地址,一般以頁爲單位進行處理。MMU以頁大小爲單位來管理系統中的也表。linux

       32位系統:頁大小4KB緩存

       64位系統:頁大小8KB數據結構

內核用相應的數據結構表示系統中的每一個物理頁:函數

  <linux/mm_types.h>性能

  struct page {}大數據

內核經過這樣的數據結構管理系統中全部的頁,所以內核判斷一個頁是否空閒,誰有擁有這個頁atom

,擁有者多是:用戶空間進程、動態分配的內核數據、靜態內核代碼、頁高速緩存……spa

系統中每個物理頁都要分配這樣一個結構體,進行內存管理。線程

二 區

       Linux內存尋址存在問題:

一些硬件只能用某些特定的內存來執行DMA(直接內存訪問)

一些體系結構其內存的物理尋址範圍必須你尋址範圍大得多。這樣致使一些內存不能永久映射到內核空間上。

       一般32位Linux內核地址空間劃分0~3G爲用戶空間,3~4G爲內核空間。當內核模塊代碼或線程訪問內存時,

代碼中的內存地址都爲邏輯地址,而對應到真正的物理內存地址,須要地址一對一的映射。所以內核空間地址爲3~4G,

最多隻能映射到1G空間的內存,超出1G大小的內存將如何去問呢!

       因爲存在上述條件的限制。Linux將內核空間地址劃分爲三個區:

ZONE_DMA、ZONE_NORMAL和ZONE_HIGHMEM。

       ZONE_HIGHMEM即爲高端內存,這就是內存高端內存概念的由來。

 

在x86結構中,三種類型的區域以下:

  ZONE_DMA        內存開始的16MB

  ZONE_NORMAL       16MB~896MB

  ZONE_HIGHMEM       896MB ~ 結束

一樣每一個區包含衆多頁,造成不一樣內存池,按照用途進行內存分配。

用相應的數據結構來表示區:

  <linux/mmzone.h>

  struct zone {}

三 獲取頁/內存

static inline struct page *alloc_pages(gfp_t gfp_mask, unsigned int order)

該函數分配2的order次方個連續的物理頁,返回指向第一個頁的page結構體指針。

 

void *page_address(const struct page *page)

返回指向給定物理頁當前所在的邏輯地址

extern unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order);

extern unsigned long get_zeroed_page(gfp_t gfp_mask);

釋放:

extern void __free_pages(struct page *page, unsigned int order);

extern void free_pages(unsigned long addr, unsigned int order);

 

內存的分配可能失敗,內存的釋放要準確!

 

1 kmalloc

kmalloc()函數與用戶空間malloc一組函數相似,得到以字節爲單位的一塊內核內存。

void *kmalloc(size_t size, gfp_t flags)

void kfree(const void *objp)

 

分配內存物理上連續。

gfp_t標誌:代表分配內存的方式。如:

GFP_ATOMIC:分配內存優先級高,不會睡眠

GFP_KERNEL:經常使用的方式,可能會阻塞。

 

2 vmalloc    

 

void *vmalloc(unsigned long size)

void vfree(const void *addr)

vmalloc()與kmalloc方式相似,vmalloc分配的內存虛擬地址是連續的,而物理地址則無需連續,與用戶空間分配函數一致。

vmalloc經過分配非連續的物理內存塊,在修正頁表,把內存映射到邏輯地址空間的連續區域中,虛擬地址是連續的。

       是否必需要連續的物理地址和具體使用場景有關。在不理解虛擬地址的硬件設備中,內存區都必須是連續的。

       經過創建頁錶轉換成虛擬地址空間上連續,確定存在一些消耗,帶來性能上影響。

因此一般內核使用kmalloc來申請內存,在須要大塊內存時使用vmalloc來分配。

 

四 slab層

       內核中常常進行內存的分配和釋放。爲了便於數據的頻繁分配和回收,一般創建一個空

 

閒鏈表——內存池。當不使用的已分配的內存時,將其放入內存池中,而不是直接釋放掉。

       Linux內核提供了slab層來管理內存的分配和釋放。

頻繁分配和回收必然致使內存碎片,緩存他們.

slab層得設計實現

       slab層把不一樣的對象劃分爲所謂的高速緩存組。每一個高速緩存組存放不一樣類型的對象。高速緩存劃分爲slab,

slab由一個或多個物理上連續的頁組成。每一個slab處於三種狀態之一:滿,部分滿,空。

高速緩存,slab,對象之間的關係:

          圖  1. slab 分配器的主要結構

 

 

       與傳統的內存管理模式相比, slab 緩存分配器提供了不少優勢。首先,內核一般依賴於對小對象的分配,

它們會在系統生命週期內進行無數次分配。slab 緩存分配器經過對相似大小的對象進行緩存而提供這種功能,

從而避免了常見的碎片問題。slab 分配器還支持通用對象的初始化,從而避免了爲同一目而對一個對象重複

進行初始化。最後,slab 分配器還能夠支持硬件緩存對齊和着色,這容許不一樣緩存中的對象佔用相同的緩存行,

從而提升緩存的利用率並得到更好的性能。

 

slab數據結構和接口:

每一個高速緩存用kmem_cache結構來表示:

       struct kmem_cache {

              struct kmem_list3 **nodelists;

              ……

       }

緩存區包含三種slab:滿,未滿,空閒

struct kmem_list3 {

       struct list_head slabs_partial; /* partial list first, better asm code */

       struct list_head slabs_full;

       struct list_head slabs_free;

       ……

};

每個slab包含多個對象:

struct slab {

              struct list_head list;

              unsigned long colouroff;

              void *s_mem;            /* including colour offset */

              unsigned int inuse;     /* num of objs active in slab */

              kmem_bufctl_t free;

              unsigned short nodeid;

};

 

相關接口:mm/slab.c

              內核函數 kmem_cache_create 用來建立一個新緩存。這一般是在內核初始化時執行的,或者在首次加載內核模塊時執行。

struct kmem_cache *kmem_cache_create (

  const char *name,

  size_t size,

  size_t align,

  unsigned long flags,

  void (*ctor)(void *))

      

name 參數定義了緩存名稱,proc 文件系統(在 /proc/slabinfo 中)使用它標識這個緩存。

size 參數指定了爲這個緩存建立的對象的大小,

align 參數定義了每一個對象必需的對齊。

flags 參數指定了爲緩存啓用的選項:

  kmem_cache_create 的部分選項(在 flags 參數中指定)

  SLAB_RED_ZONE    在對象頭、尾插入標誌,用來支持對緩衝區溢出的檢查。

  SLAB_POISON  使用一種己知模式填充 slab,容許對緩存中的對象進行監視(對象屬對象全部,不過能夠在外部進行修改)。

  SLAB_HWCACHE_ALIGN      指定緩存對象必須與硬件緩存行對齊。

ctor 和 dtor 參數定義了一個可選的對象構造器和析構器。構造器和析構器是用戶提供的回調函數。當從緩存中分配新對象時,能夠經過構造器進行初始化。

    要從一個命名的緩存中分配一個對象,可使用 kmem_cache_alloc 函數。

 

void kmem_cache_alloc( struct kmem_cache *cachep, gfp_t flags );

這個函數從緩存中返回一個對象。注意若是緩存目前爲空,那麼這個函數就會調用 cache_alloc_refill 向緩存中增長內存。

kmem_cache_alloc 的 flags 選項與 kmalloc 的

cachep:所創建的緩存區

flags參數:

  GFP_USER 爲用戶分配內存(這個調用可能會睡眠)。

  GFP_KERNEL    從內核 RAM 中分配內存(這個調用可能會睡眠)。

  GFP_ATOMIC   使該調用強制處於非睡眠狀態(對中斷處理程序很是有用)。

  GFP_HIGHUSER      從高端內存中分配內存。

 

五 高端內存的映射

永久映射:可能會阻塞

  映射一個給定的page結構到內核地址空間:

  void *kmap(struct page *page)

  解除映射:

  void kunmap(struct page *page)

 

臨時映射:不會阻塞     

void *kmap_atomic(struct page *page)

 

六 分配函數的選擇

  l  連續的物理頁:kmalloc或者低級頁分配器

  l  高端內存分配:alloc_pages 指向page結構指針,不是邏輯地址指針。再經過kmap()把高端地址內存映射到內核的邏輯地址空間。

  l  無需連續物理地址:vmalloc 虛擬地址連續物理地址可能不連續,相對存在性能損失

  l  頻繁建立和銷燬不少較大數據結構:創建slab緩存區,提升對象分配和回收性能。

 

Linux高端內存:
  http://ilinuxkernel.com/?p=1013
Linux slab 分配器剖析:
  https://www.ibm.com/developerworks/cn/linux/l-linux-slab-allocator/

相關文章
相關標籤/搜索