24.slab分配器(释放对象kmem_cache_free,销毁缓存kmem_cache_destroy)

如果一个分配的对象已经不再需要,那么必须使用kmem_cache_free返回给slab分配器。图3-52给出了该函数的代码流程图。
 
类似于分配,根据per-CPU缓存的状态不同,有两种可选的操作流程。如果per-CPU缓存中的对象数目低于允许的限制,则在其中存储一个指向缓存中对象的指针。
 
mm/slab.c 
static inline void __cache_free(kmem_cache_t *cachep, void *objp) 
{ 
... 
  if (likely(ac->avail < ac->limit)) { 
    ac->entry[ac->avail++] = objp; 
    return; 
  } else { 
    cache_flusharray(cachep, ac); 
    ac->entry[ac->avail++] = objp; 
  } 
}
否则,必须将一些对象(准确的数目由array_cache->batchcount给出)从缓存移回slab,从编号最低的数组元素开始:缓存的实现依据先进先出原理,这些对象在数组中已经很长时间,因此不太可能仍然驻留在CPU高速缓存中。
具体的实现委托给cache_flusharray。该函数又调用了free_block,将对象从缓存移动到原来的slab,并将剩余的对象向数组起始处移动。例如,如果缓存中有30个对象的空间,而batchcount为15,则位置0到14的对象将移回slab。剩余编号15到29的对象则在缓存中向上移动,现在占据位置0到14。
 
将对象从缓存移回到slab是有益的,因此仔细考察一下free_block是值得的。该函数所需的参数是缓存的kmem_cache_t实例、指向缓存中对象指针数组的指针、表示数组中对象数目的整数和内存所属的结点。
mm/slab.c 
static void free_block(kmem_cache_t *cachep, void **objpp, int nr_objects, int node) 
{ 
  int i; 
  struct kmem_list3 *l3; 
  for (i = 0; i < nr_objects; i++) { 
    void *objp = objpp[i]; 
    struct slab *slabp; 
...
对每个对象必须执行下列操作:
mm/slab.c 
  slabp = virt_to_slab(objp);
    l3 = cachep->nodelists[node];
    list_del(&slabp->list);
    slab_put_obj(cachep, slabp, objp, node);
   slabp->inuse--;
    l3->free_objects++;
在确定对象所属的slab之前,首先必须调用virt_to_page找到对象所在的页。与slab之间的关联使用前文所述的page_get_slab确定。
该slab(临时)从缓存的链表移除。slab_put_obj反映了在空闲链表中的这种操作:用于分配的第一个对象是刚刚删除的,而列表中的下一个对象则是此前的第一个对象。此后,该slab重新插入到缓存的链表中:
mm/slab.c 
... 
  /* 修正slab所处的链表 */ 
  if (slabp->inuse == 0) { 
    if (l3->free_objects > l3->free_limit) { 
       l3->free_objects -= cachep->num; 
       slab_destroy(cachep, slabp); 
    } else { 
       list_add(&slabp->list, &l3->slabs_free); 
    } 
  } else { 
     list_add(&slabp->list, &l3->slabs_partial); 
  } 
  } 
}

 

如果在删除之后,slab中的所有对象都是未使用的(slab->inuse == 0),则将slab置于slabs_free链表中。
例外情况:缓存中空闲对象的数目超过预定义的限制cachep->free_limit。在这种情况下,使用slab_destroy将整个slab返回给伙伴系统。
如果slab同时包含使用和未使用对象,则插入到slabs_partial链表
 销毁缓存
如果要销毁只包含未使用对象的一个缓存,则必须调用kmem_cache_destroy函数。该函数主要在删除模块时调用,此时需要将分配的内存都释放。由于该函数的实现没什么新东西,下面我们只是概述一下删除缓存的主要步骤。
 依次扫描slabs_free链表上的slab。首先对每个slab上的每个对象调用析构器函数,然后将slab的内存空间返回给伙伴系统。
 释放用于per-CPU缓存的内存空间。
 从cache_cache链表移除相关数据。
posted @ 2022-03-23 00:20  while(true);;  阅读(167)  评论(0)    收藏  举报