1. 程式人生 > >[Linux記憶體]slab分配器學習筆記(一)--概念

[Linux記憶體]slab分配器學習筆記(一)--概念

http://blog.csdn.net/vanbreaker/article/details/7664296
1,為什麼需要slab分配器:
 利用夥伴系統進行分配記憶體只能按照頁的單位進行分配,這樣會造成很多的記憶體浪費,多了很多記憶體碎片,比如只需要申請10位元組的,結果分配了一個頁。
 
2,slab分配器和夥伴系統分配的差別
 slab分配器是基於物件進行管理的,相同型別的物件歸為一類(如程序描述符就是一類),每當要申請這樣一個物件,slab分配器就從一個slab列表中分配一個這樣大小的單元出去,而當要釋放時,將其重新儲存在該列表中,而不是直接返回給夥伴系統。slab分配物件時,會使用最近釋放的物件記憶體塊,因此其駐留在CPU快取記憶體的概率較高
 Slab分配器由很多快取(動態連結串列)組成,不同的快取大小不同(如名為kmalloc-128的快取所管理物件的長度為128位元組)。Linux的Slab快取分為專用和通用兩種,通用快取通過函式kmem_cache_init() 進行初始化,適用於特定物件,比如各種描述符,存放程序描述符的快取大小就是sizeof(task_struct);而專用快取則通過函式 kmem_cache_create()建立。通用Slab的物件大小預先定義好的,一般是2的次冪(cache對齊的需要),Linux使用13級, 如下:32、64、128、256、512、1024、2048、4096、8192、16384、32768、65536、131072。這樣可以保證記憶體碎片小於百分之50


3,slab的概念

slab分配器把記憶體區看作物件(object),這些物件由一組資料結構和幾個建構函式或者解構函式組成,分別用於初始化和回收記憶體。slab分配器把物件分組放進告訴快取,包含快取記憶體的主記憶體區被劃分為多個slab,每個slab由一個或者多個連續的頁組成,這些頁框包含已分配物件和空閒物件。

每一個slab物件都有一個kmem_bufctl_t描述符,每個slab的物件描述符都存放在slab描述符的後面。因此((kmem_bufctl_t*)(slab + 1)就指向slabkmem_bufctl_t描述符地址,語句((kmem_bufctl_t*)(slab + 1)[i]

就是訪問第i個物件的索引。


4,slab分配器用的相關資料結構
用於描述和管理cache的資料結構是struct kmem_cache

struct kmem_cache {  
/* 1) per-cpu data, touched during every alloc/free */  
    /*per-CPU資料,記錄了本地快取記憶體的資訊,也用於跟蹤最近釋放的物件,每次分配和釋放都要直接訪問它*/  
    struct array_cache *array[NR_CPUS];   
/* 2) Cache tunables. Protected by cache_chain_mutex */  
    unsigned int batchcount;  /*本地快取記憶體轉入或轉出的大批物件數量*/  
    unsigned int limit;       /*本地快取記憶體中空閒物件的最大數目*/  
    unsigned int shared;  
  
    unsigned int buffer_size;/*管理物件的大小*/  
    u32 reciprocal_buffer_size;/*buffer_size的倒數值*/  
/* 3) touched by every alloc & free from the backend */  
  
    unsigned int flags;          /* 快取記憶體的永久標識*/  
    unsigned int num;         /* 一個slab所包含的物件數目 */  
  
/* 4) cache_grow/shrink */  
    /* order of pgs per slab (2^n) */  
    unsigned int gfporder;   /*一個slab包含的連續頁框數的對數*/  
   
    /* force GFP flags, e.g. GFP_DMA */  
    gfp_t gfpflags;          /*與夥伴系統互動時所提供的分配標識*/  
  
    size_t colour;         /* 顏色的個數*/  
    unsigned int colour_off; /* 著色的偏移量 */  
      
    /*如果將slab描述符儲存在外部,該指標指向儲存slab描述符的cache, 
      否則為NULL*/  
    struct kmem_cache *slabp_cache;  
    unsigned int slab_size;  /*slab管理區的大小*/  
    unsigned int dflags;     /*動態標識*/  
  
    void (*ctor)(void *obj); /*建立快取記憶體時的建構函式指標*/  
  
/* 5) cache creation/removal */  
    const char *name;         /*快取記憶體名*/  
    struct list_head next;    /*用於將快取記憶體鏈入cache chain*/  
  
/* 6) statistics */  
   
     /*struct kmem_list3用於組織該快取記憶體中的slab*/  
    struct kmem_list3 *nodelists[MAX_NUMNODES];  
    /* 
     * Do not add fields after nodelists[] 
     */  
}; 

struct kmem_list3 {  
    struct list_head slabs_partial;/*slab連結串列,包含空閒物件和已分配物件的slab描述符*/  
    struct list_head slabs_full;   /*slab連結串列,只包含非空閒的slab描述符*/  
    struct list_head slabs_free;   /*slab連結串列,只包含空閒的slab描述符*/  
    unsigned long free_objects;    /*快取記憶體中空閒物件的個數*/  
    unsigned int free_limit;       /*空閒物件的上限*/  
    unsigned int colour_next;       /*下一個slab使用的顏色*/  
    spinlock_t list_lock;  
    struct array_cache *shared; /* shared per node */  
    struct array_cache **alien; /* on other nodes */  
    unsigned long next_reap;    /* updated without locking */  
    int free_touched;       /* updated without locking */  
};  

描述和管理單個slab的結構是struct slab
struct slab {  
    struct list_head list;  /*用於將slab鏈入kmem_list3的連結串列*/  
    unsigned long colouroff;/*該slab的著色偏移*/  
    void *s_mem;            /*指向slab中的第一個物件*/  
    unsigned int inuse;     /*已分配出去的物件*/  
    kmem_bufctl_t free;     /*下一個空閒物件的下標*/  
    unsigned short nodeid;  /*節點標識號*/  
}; 

各個資料結構之間關係:



 slab描述符可以放在以下兩個地方
1,外部slab描述符,存在普通的快取記憶體
2,內部slab描述符,位於分配給slab的第一個頁框的起始位置

struct kmem_cache中定義了一個struct array_cache指標陣列,陣列的元素個數對應了系統的CPU數,和夥伴系統中的每CPU頁框快取記憶體類似,該結構用來描述每個CPU的本地快取記憶體,在每個array_cache的末端都用一個指標陣列記錄了slab中的空閒物件,分配物件時,採用LIFO方式,也就是將該陣列中的最後一個索引對應的物件分配出去,以保證該物件還駐留在快取記憶體中的可能性。實際上,每次分配記憶體都是直接與本地CPU快取記憶體進行互動,只有當其空閒記憶體不足時,才會從kmem_list中的slab中引入一部分物件到本地快取記憶體中,而kmem_list中的空閒物件也不足了,那麼就要從夥伴系統中引入新的頁來建立新的slab了,這一點也和夥伴系統的
每CPU頁框快取記憶體很類似。

struct array_cache {  
    unsigned int avail;/*本地快取記憶體中可用的空閒物件數*/  
    unsigned int limit;/*空閒物件的上限*/  
    unsigned int batchcount;/*一次轉入和轉出的物件數量*/  
    unsigned int touched;   /*標識本地CPU最近是否被使用*/  
    spinlock_t lock;  
    void *entry[];  /*這是一個偽陣列,便於對後面用於跟蹤空閒物件的指標陣列的訪問  
             */  
}; 

因此,物件分配的次序為:(1)特定於CPU/節點的快取列表中的物件(2)當前已經存在於slab快取中中的未用物件(3)從夥伴系統獲得記憶體,然後建立的物件

物件在slab中不是連續排列的,其排列如圖所示:


5,夥伴系統 slab分配器的關係