歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> Linux教程

Linux內存管理之slab機制(分配對象)

Linux內核從slab中分配內存空間上層函數由kmalloc()或kmem_cache_alloc()函數實現。

kmalloc()->__kmalloc()->__do_kmalloc()

  1. /** 
  2.  * __do_kmalloc - allocate memory 
  3.  * @size: how many bytes of memory are required. 
  4.  * @flags: the type of memory to allocate (see kmalloc). 
  5.  * @caller: function caller for debug tracking of the caller 
  6.  */  
  7. static __always_inline void *__do_kmalloc(size_t size, gfp_t flags,  
  8.                       void *caller)  
  9. {  
  10.     struct kmem_cache *cachep;  
  11.     void *ret;  
  12.   
  13.     /* If you want to save a few bytes .text space: replace 
  14.      * __ with kmem_. 
  15.      * Then kmalloc uses the uninlined functions instead of the inline 
  16.      * functions. 
  17.      */  
  18.      /*查找指定大小的通用cache,關於sizes[]數組,在前面 
  19.     的初始化中就已經分析過了*/  
  20.     cachep = __find_general_cachep(size, flags);  
  21.     if (unlikely(ZERO_OR_NULL_PTR(cachep)))  
  22.         return cachep;  
  23.     /*實際的分配工作*/  
  24.     ret = __cache_alloc(cachep, flags, caller);  
  25.   
  26.     trace_kmalloc((unsigned long) caller, ret,  
  27.               size, cachep->buffer_size, flags);  
  28.   
  29.     return ret;  
  30. }  

實際的分配工作:__do_cache_alloc()->__cache_alloc()->____cache_alloc()

  1. /*從指定cache中分配對象*/  
  2. static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags)  
  3. {  
  4.     void *objp;  
  5.     struct array_cache *ac;  
  6.   
  7.     check_irq_off();  
  8.     /* 獲得本CPU的local cache */  
  9.     ac = cpu_cache_get(cachep);  
  10.     /* 如果local cache中有可用的空閒對象 */  
  11.     if (likely(ac->avail)) {  
  12.         /* 更新local cache命中計數 */  
  13.         STATS_INC_ALLOCHIT(cachep);  
  14.         /* touched置1表示最近使用了local cache,這會影響填充 
  15.         local cache時的數目,最近使用的填充較多的對象 */  
  16.         ac->touched = 1;  
  17.          /* 從local cache的entry數組中提取最後面的空閒對象 */  
  18.         objp = ac->entry[--ac->avail];  
  19.     } else {  
  20.          /* local cache中沒有空閒對象,更新未命中計數 */  
  21.         STATS_INC_ALLOCMISS(cachep);  
  22.         /* 從slab三鏈中提取空閒對象填充到local cache中 */  
  23.         objp = cache_alloc_refill(cachep, flags);  
  24. #if 0/*這裡是我新加的,這裡可能是這個版本的一個bug,在最新的內核裡面這塊已經加上了*/   
  25.        /* 
  26.                        * the 'ac' may be updated by cache_alloc_refill(), 
  27.                        * and kmemleak_erase() requires its correct value. 
  28.                        */  
  29.              /* cache_alloc_refill的cache_grow打開了中斷,local cache指針可能發生了變化,需要重新獲得 */  
  30.                       ac = cpu_cache_get(cachep);  
  31. #endif       
  32.     }  
  33.     /* 
  34.      * To avoid a false negative, if an object that is in one of the 
  35.      * per-CPU caches is leaked, we need to make sure kmemleak doesn't 
  36.      * treat the array pointers as a reference to the object. 
  37.      */ /* 分配出去的對象,其entry指針指向空 */  
  38.     kmemleak_erase(&ac->entry[ac->avail]);  
  39.     return objp;  
  40. }  

該函數的執行流程:

1,從本地CPU cache中查找是否有空閒的對象;

2,如果本地CPU cache 中沒有空閒對象,從slab三鏈中提取空閒對象,此操作由函數cache_alloc_refill()實現

1)如果存在共享本地cache,那麼將共享本地cache中的對象批量復制到本地cache。

2)如果沒有shared local cache,或是其中沒有空閒的對象,從slab鏈表中分配,其中,從slab中分配時,先查看部分空余鏈表,然後再查看空余鏈表。將slab鏈表中的數據先放到本地CPU cache中。

3) 如果本地CPU cache中任然沒有數據,那麼只有重新創建一個slab,然後再試。

  1. /*從slab三鏈中提取一部分空閒對象填充到local cache中*/  
  2. static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags)  
  3. {  
  4.     int batchcount;  
  5.     struct kmem_list3 *l3;  
  6.     struct array_cache *ac;  
  7.     int node;  
  8.   
  9. retry:  
  10.     check_irq_off();  
  11.      /* 獲得本內存節點,UMA只有一個節點 */  
  12.     node = numa_node_id();  
  13.      /* 獲得本CPU的local cache */  
  14.     ac = cpu_cache_get(cachep);  
  15.     /* 批量填充的數目,local cache是按批填充的 */  
  16.     batchcount = ac->batchcount;  
  17.     if (!ac->touched && batchcount > BATCHREFILL_LIMIT) {  
  18.         /* 
  19.          * If there was little recent activity on this cache, then 
  20.          * perform only a partial refill.  Otherwise we could generate 
  21.          * refill bouncing. 
  22.          */  
  23.          /* 最近未使用過此local cache,沒有必要添加過多的對象 
  24.          ,添加的數目為默認的限定值 */  
  25.         batchcount = BATCHREFILL_LIMIT;  
  26.     }  
  27.     /* 獲得本內存節點、本cache的slab三鏈 */  
  28.     l3 = cachep->nodelists[node];  
  29.   
  30.     BUG_ON(ac->avail > 0 || !l3);  
  31.     spin_lock(&l3->list_lock);  
  32.   
  33.     /* See if we can refill from the shared array */  
  34.     /* shared local cache用於多核系統中,為所有cpu共享 
  35.     ,如果slab cache包含一個這樣的結構 
  36.     ,那麼首先從shared local cache中批量搬運空閒對象到local cache中 
  37.     。通過shared local cache使填充工作變得簡單。*/  
  38.     if (l3->shared && transfer_objects(ac, l3->shared, batchcount))  
  39.         goto alloc_done;  
  40.   
  41.     /* 如果沒有shared local cache,或是其中沒有空閒的對象 
  42.     ,從slab鏈表中分配 */  
  43.     while (batchcount > 0) {  
  44.         struct list_head *entry;  
  45.         struct slab *slabp;  
  46.         /* Get slab alloc is to come from. */  
  47.           
  48.         /* 先從部分滿slab鏈表中分配 */  
  49.         entry = l3->slabs_partial.next;  
  50.         /* next指向頭節點本身,說明部分滿slab鏈表為空 */  
  51.         if (entry == &l3->slabs_partial) {  
  52.             /* 表示剛剛訪問了slab空鏈表 */  
  53.             l3->free_touched = 1;  
  54.             /* 檢查空slab鏈表 */  
  55.             entry = l3->slabs_free.next;  
  56.             /* 空slab鏈表也為空,必須增加slab了 */  
  57.             if (entry == &l3->slabs_free)  
  58.                 goto must_grow;  
  59.         }  
  60.         /* 獲得鏈表節點所在的slab */  
  61.         slabp = list_entry(entry, struct slab, list);  
  62.         /*調試用*/  
  63.         check_slabp(cachep, slabp);  
  64.         check_spinlock_acquired(cachep);  
  65.   
  66.         /* 
  67.          * The slab was either on partial or free list so 
  68.          * there must be at least one object available for 
  69.          * allocation. 
  70.          */  
  71.         BUG_ON(slabp->inuse >= cachep->num);  
  72.   
  73.         while (slabp->inuse < cachep->num && batchcount--) {  
  74.             /* 更新調試用的計數器 */  
  75.             STATS_INC_ALLOCED(cachep);  
  76.             STATS_INC_ACTIVE(cachep);  
  77.             STATS_SET_HIGH(cachep);  
  78.             /* 從slab中提取一個空閒對象,將其虛擬地址插入到local cache中 */  
  79.             ac->entry[ac->avail++] = slab_get_obj(cachep, slabp,  
  80.                                 node);  
  81.         }  
  82.         check_slabp(cachep, slabp);  
  83.   
  84.         /* move slabp to correct slabp list: */  
  85.         /* 從原鏈表中刪除此slab節點,list表示此 
  86.         slab位於哪個鏈表(滿、部分滿、空)中 */  
  87.         list_del(&slabp->list);  
  88.         /*因為從中刪除了一個slab,需要從新檢查*/  
  89.         if (slabp->free == BUFCTL_END)  
  90.             /* 此slab中已經沒有空閒對象,添加到“full”slab鏈表中 */  
  91.             list_add(&slabp->list, &l3->slabs_full);  
  92.         else  
  93.             /* 還有空閒對象,添加到“partial”slab鏈表中 */  
  94.             list_add(&slabp->list, &l3->slabs_partial);  
  95.     }  
  96.   
  97. must_grow:  
  98.     /* 前面從slab鏈表中添加avail個空閒對象到local cache中 
  99.     ,更新slab鏈表的空閒對象數 */  
  100.     l3->free_objects -= ac->avail;  
  101. alloc_done:  
  102.     spin_unlock(&l3->list_lock);  
  103.     /* local cache中仍沒有可用的空閒對象,說明slab 
  104.     三鏈中也沒有空閒對象,需要創建新的空slab了 */  
  105.     if (unlikely(!ac->avail)) {  
  106.         int x;  
  107.         /* 創建一個空slab */  
  108.         x = cache_grow(cachep, flags | GFP_THISNODE, node, NULL);  
  109.   
  110.         /* cache_grow can reenable interrupts, then ac could change. */  
  111.         /* 上面的操作使能了中斷,此期間local cache指針可能發生了變化,需要重新獲得 */  
  112.         ac = cpu_cache_get(cachep);  
  113.         /* 無法新增空slab,local cache中也沒有空閒對象,表明系統已經無法分配新的空閒對象了 */  
  114.         if (!x && ac->avail == 0)    /* no objects in sight? abort */  
  115.             return NULL;  
  116.         /* 走到這有兩種可能,第一種是無論新增空slab成功或失敗,只要avail不為0 
  117.         ,表明是其他進程重填了local cache,本進程就不需要重填了 
  118.         ,不執行retry流程。第二種是avail為0,並且新增空slab成功 
  119.         ,則進入retry流程,利用新分配的空slab填充local cache */  
  120.         if (!ac->avail)      /* objects refilled by interrupt? */  
  121.             goto retry;  
  122.     }  
  123.     /* 重填了local cache,設置近期訪問標志 */  
  124.     ac->touched = 1;  
  125.     /* 返回local cache中最後一個空閒對象的虛擬地址 */  
  126.     return ac->entry[--ac->avail];  
  127. }  
Copyright © Linux教程網 All Rights Reserved