【原創】(十二)Linux內存管理之vmap與vmalloc

背景

  • Read the fucking source code! --By 魯迅
  • A picture is worth a thousand words. --By 高爾基

說明:node

  1. Kernel版本:4.14
  2. ARM64處理器,Contex-A53,雙核
  3. 使用工具:Source Insight 3.5, Visio

1. 概述

在以前的系列文章中,分析到了Buddy System的頁框分配,Slub分配器的小塊內存對象分配,這些分配的地址都是物理內存連續的。當內存碎片後,連續物理內存的分配就會變得困難,可使用vmap機制,將不連續的物理內存頁框映射到連續的虛擬地址空間中。vmalloc的分配就是基於這個機制來實現的。算法

還記得下邊這張圖嗎?
數組

vmap/vmalloc的區域就是在VMALLOC_START ~ VMALLOC_END之間。緩存

開啓探索之旅吧。數據結構

2. 數據結構

2.1 vmap_area/vm_struct

這兩個數據結構比較簡單,直接上代碼:less

struct vm_struct {
    struct vm_struct    *next;
    void            *addr;
    unsigned long       size;
    unsigned long       flags;
    struct page     **pages;
    unsigned int        nr_pages;
    phys_addr_t     phys_addr;
    const void      *caller;
};

struct vmap_area {
    unsigned long va_start;
    unsigned long va_end;
    unsigned long flags;
    struct rb_node rb_node;         /* address sorted rbtree */
    struct list_head list;          /* address sorted list */
    struct llist_node purge_list;    /* "lazy purge" list */
    struct vm_struct *vm;
    struct rcu_head rcu_head;
};

struct vmap_area用於描述一段虛擬地址的區域,從結構體中va_start/va_end也能看出來。同時該結構體會經過rb_node掛在紅黑樹上,經過list掛在鏈表上。
struct vmap_areavm字段是struct vm_struct結構,用於管理虛擬地址和物理頁之間的映射關係,能夠將struct vm_struct構成一個鏈表,維護多段映射。函數

關係以下圖:
工具

2.2 紅黑樹

紅黑樹,本質上是一種二叉查找樹,它在二叉查找樹的基礎上增長了着色相關的性質,提高了紅黑樹在查找,插入,刪除時的效率。在紅黑樹中,節點已經進行排序,對於每一個節點,左側的的元素都在節點以前,右側的元素都在節點以後。
紅黑樹必須知足如下四條規則:性能

  1. 每一個節點不是紅就是黑;
  2. 紅黑樹的根必須是黑;
  3. 紅節點的子節點必須爲黑;
  4. 從節點到子節點的每一個路徑都包含相同數量的黑節點,統計黑節點個數時,空指針也算黑節點;

定義以下:3d

struct rb_node {
    unsigned long  __rb_parent_color;
    struct rb_node *rb_right;
    struct rb_node *rb_left;
} __attribute__((aligned(sizeof(long))));
    /* The alignment might seem pointless, but allegedly CRIS needs it */

因爲內核會頻繁的進行vmap_area的查找,紅黑樹的引入就是爲了解決當查找數量很是多時效率低下的問題,在紅黑樹中,搜索元素,插入,刪除等操做,都會變得很是高效。至於紅黑樹的算法操做,本文就再也不深刻分析,知道它的用途便可。

3. vmap/vunmap分析

3.1 vmap

vmap函數,完成的工做是,在vmalloc虛擬地址空間中找到一個空閒區域,而後將page頁面數組對應的物理內存映射到該區域,最終返回映射的虛擬起始地址。

總體流程以下:

操做流程比較簡單,來一個樣例分析,就清晰明瞭了:

vmap調用中,關鍵函數爲alloc_vmap_area,它先經過vmap_area_root二叉樹來查找第一個區域first vm_area,而後根據這個first vm_area去查找vmap_area_list鏈表中知足大小的空間區域。

alloc_vmap_area函數中,有幾個全局的變量:

static struct rb_node *free_vmap_cache;
static unsigned long cached_hole_size;
static unsigned long cached_vstart;
static unsigned long cached_align;

用於緩存上一次分配成功的vmap_area,其中cached_hole_size用於記錄緩存vmap_area對應區域以前的空洞的大小。緩存機制固然也是爲了提升分配的效率。

3.2 vunmap

vunmap執行的是跟vmap相反的過程:從vmap_area_root/vmap_area_list中查找vmap_area區域,取消頁表映射,再從vmap_area_root/vmap_area_list中刪除掉vmap_area,頁面返還給夥伴系統等。因爲映射關係有改動,所以還須要進行TLB的刷新,頻繁的TLB刷新會下降性能,所以將其延遲進行處理,所以稱爲lazy tlb

來看看逆過程的流程:

4. vmalloc/vfree分析

4.1 vmalloc

vmalloc用於分配一個大的連續虛擬地址空間,該空間在物理上不連續的,所以也就不能用做DMA緩衝區。vmalloc分配的線性地址區域,在文章開頭的圖片中也描述了:VMALLOC_START ~ VMALLOC_END

直接分析調用流程:

從過程當中能夠看出,vmallocvmap的操做,大部分的邏輯操做是同樣的,好比從VMALLOC_START ~ VMALLOC_END區域之間查找並分配vmap_area, 好比對虛擬地址和物理頁框進行映射關係的創建。不一樣之處,在於vmap創建映射時,page是函數傳入進來的,而vmalloc是經過調用alloc_page接口向Buddy System申請分配的。

  • vmalloc VS kmalloc
    到如今,咱們應該能清楚vmallockmalloc的差別了吧,kmalloc會根據申請的大小來選擇基於slub分配器或者基於Buddy System來申請連續的物理內存。而vmalloc則是經過alloc_page申請order = 0的頁面,再映射到連續的虛擬空間中,物理地址不連續,此外vmalloc能夠休眠,不該在中斷處理程序中使用。
    vmalloc相比,kmalloc使用ZONE_DMA和ZONE_NORMAL空間,性能更快,缺點是連續物理內存空間的分配容易帶來碎片問題,讓碎片的管理變得困難。

4.2 vfree

直接上代碼:

void vfree(const void *addr)
{
    BUG_ON(in_nmi());

    kmemleak_free(addr);

    if (!addr)
        return;
    if (unlikely(in_interrupt()))
        __vfree_deferred(addr);
    else
        __vunmap(addr, 1);
}

若是在中斷上下文中,則推遲釋放,不然直接調用__vunmap,因此它的邏輯基本和vunmap一致,再也不贅述了。

相關文章
相關標籤/搜索