- 论坛徽章:
- 0
|
Linux内存管理之slab机制(释放对象)
Linux内核中将对象释放到slab中上层所用函数为kfree()或kmem_cache_free()。两个函数都会调用__cache_free()函数。
代码执行流程:
1,当本地CPU cache中空闲对象数小于规定上限时,只需将对象放入本地CPU cache中;
2,当local cache中对象过多(大于等于规定上限),需要释放一批对象到slab三链中。由函数cache_flusharray()实现。
1)如果三链中存在共享本地cache,那么首先选择释放到共享本地cache中,能释放多少是多少;
2)如果没有shared local cache,释放对象到slab三链中,实现函数为free_block()。对于free_block()函数,当三链中的空闲对象数过多时,销毁此cache。不然,添加此slab到空闲链表。因为在分配的时候我们看到将slab结构从cache链表中脱离了,在这里,根据page描述符的lru找到slab并将它添加到三链的空闲链表中。
主实现
view plaincopy to clipboard- 01./*
- 02. * Release an obj back to its cache. If the obj has a constructed state, it must
- 03. * be in this state _before_ it is released. Called with disabled ints.
- 04. */
- 05.static inline void __cache_free(struct kmem_cache *cachep, void *objp)
- 06.{
- 07. /* 获得本CPU的local cache */
- 08. struct array_cache *ac = cpu_cache_get(cachep);
- 09.
- 10. check_irq_off();
- 11. kmemleak_free_recursive(objp, cachep->flags);
- 12. objp = cache_free_debugcheck(cachep, objp, __builtin_return_address(0));
- 13.
- 14. kmemcheck_slab_free(cachep, objp, obj_size(cachep));
- 15.
- 16. /*
- 17. * Skip calling cache_free_alien() when the platform is not numa.
- 18. * This will avoid cache misses that happen while accessing slabp (which
- 19. * is per page memory reference) to get nodeid. Instead use a global
- 20. * variable to skip the call, which is mostly likely to be present in
- 21. * the cache.
- 22. *//* NUMA相关 */
- 23. if (nr_online_nodes > 1 && cache_free_alien(cachep, objp))
- 24. return;
- 25.
- 26. if (likely(ac->avail < ac->limit)) {
- 27. /* local cache中的空闲对象数小于上限时
- 28. ,只需将对象释放回entry数组中 */
- 29. STATS_INC_FREEHIT(cachep);
- 30. ac->entry[ac->avail++] = objp;
- 31. return;
- 32. } else {
- 33. /* 大于等于上限时, */
- 34. STATS_INC_FREEMISS(cachep);
- 35. /* local cache中对象过多,需要释放一批对象到slab三链中。*/
- 36. cache_flusharray(cachep, ac);
- 37. ac->entry[ac->avail++] = objp;
- 38. }
- 39.}
复制代码 释放对象到三链中
view plaincopy to clipboard- 01./*local cache中对象过多,需要释放一批对象到slab三链中。*/
- 02.static void cache_flusharray(struct kmem_cache *cachep, struct array_cache *ac)
- 03.{
- 04. int batchcount;
- 05. struct kmem_list3 *l3;
- 06. int node = numa_node_id();
- 07. /* 每次释放多少个对象 */
- 08. batchcount = ac->batchcount;
- 09.#if DEBUG
- 10. BUG_ON(!batchcount || batchcount > ac->avail);
- 11.#endif
- 12. check_irq_off();
- 13. /* 获得此cache的slab三链 */
- 14. l3 = cachep->nodelists[node];
- 15. spin_lock(&l3->list_lock);
- 16. if (l3->shared) {
- 17. /* 如果存在shared local cache,将对象释放到其中 */
- 18. struct array_cache *shared_array = l3->shared;
- 19. /* 计算shared local cache中还有多少空位 */
- 20. int max = shared_array->limit - shared_array->avail;
- 21. if (max) {
- 22. /* 空位数小于要释放的对象数时,释放数等于空位数 */
- 23. if (batchcount > max)
- 24. batchcount = max;
- 25. /* 释放local cache前面的几个对象到shared local cache中
- 26. ,前面的是最早不用的 */
- 27. memcpy(&(shared_array->entry[shared_array->avail]),
- 28. ac->entry, sizeof(void *) * batchcount);
- 29. /* 增加shared local cache可用对象数 */
- 30. shared_array->avail += batchcount;
- 31. goto free_done;
- 32. }
- 33. }
- 34. /* 无shared local cache,释放对象到slab三链中 */
- 35. free_block(cachep, ac->entry, batchcount, node);
- 36.free_done:
- 37.#if STATS
- 38. {
- 39. int i = 0;
- 40. struct list_head *p;
- 41.
- 42. p = l3->slabs_free.next;
- 43. while (p != &(l3->slabs_free)) {
- 44. struct slab *slabp;
- 45.
- 46. slabp = list_entry(p, struct slab, list);
- 47. BUG_ON(slabp->inuse);
- 48.
- 49. i++;
- 50. p = p->next;
- 51. }
- 52. STATS_SET_FREEABLE(cachep, i);
- 53. }
- 54.#endif
- 55. spin_unlock(&l3->list_lock);
- 56. /* 减少local cache可用对象数*/
- 57. ac->avail -= batchcount;
- 58. /* local cache前面有batchcount个空位,将后面的对象依次前移batchcount位 */
- 59. memmove(ac->entry, &(ac->entry[batchcount]), sizeof(void *)*ac->avail);
- 60.}
复制代码 无shared local cache,释放对象到slab三链中
view plaincopy to clipboard- 01./*
- 02. * Caller needs to acquire correct kmem_list's list_lock
- 03. */
- 04. /*释放一定数目的对象*/
- 05.static void free_block(struct kmem_cache *cachep, void **objpp, int nr_objects,
- 06. int node)
- 07.{
- 08. int i;
- 09. struct kmem_list3 *l3;
- 10. /* 逐一释放对象到slab三链中 */
- 11. for (i = 0; i < nr_objects; i++) {
- 12. void *objp = objpp[i];
- 13. struct slab *slabp;
- 14. /* 通过虚拟地址得到page,再通过page得到slab */
- 15. slabp = virt_to_slab(objp);
- 16. /* 获得slab三链 */
- 17. l3 = cachep->nodelists[node];
- 18. /* 先将对象所在的slab从链表中摘除 */
- 19. list_del(&slabp->list);
- 20. check_spinlock_acquired_node(cachep, node);
- 21. check_slabp(cachep, slabp);
- 22. /* 将对象释放到其slab中 */
- 23. slab_put_obj(cachep, slabp, objp, node);
- 24. STATS_DEC_ACTIVE(cachep);
- 25. /* 空闲对象数加一 */
- 26. l3->free_objects++;
- 27. check_slabp(cachep, slabp);
- 28.
- 29. /* fixup slab chains */
- 30. if (slabp->inuse == 0) {
- 31. /* 如果slab中均为空闲对象 */
- 32. if (l3->free_objects > l3->free_limit) {
- 33. /* 如果slab三链中空闲对象数超过上限
- 34. ,直接回收整个slab到内存
- 35. ,空闲对象数减去每个slab中对象数 */
- 36. l3->free_objects -= cachep->num;
- 37. /* No need to drop any previously held
- 38. * lock here, even if we have a off-slab slab
- 39. * descriptor it is guaranteed to come from
- 40. * a different cache, refer to comments before
- 41. * alloc_slabmgmt.
- 42. *//* 销毁struct slab对象 */
- 43. slab_destroy(cachep, slabp);
- 44. } else {
- 45. /* 将此slab添加到空slab链表中 */
- 46. list_add(&slabp->list, &l3->slabs_free);
- 47. }
- 48. } else {
- 49. /* Unconditionally move a slab to the end of the
- 50. * partial list on free - maximum time for the
- 51. * other objects to be freed, too.
- 52. *//*将此slab添加到部分满slab链表中*/
- 53. list_add_tail(&slabp->list, &l3->slabs_partial);
- 54. }
- 55. }
- 56.}
复制代码 将对象释放到其slab中
view plaincopy to clipboard- 01.static void slab_put_obj(struct kmem_cache *cachep, struct slab *slabp,
- 02. void *objp, int nodeid)
- 03.{ /* 获得对象在kmem_bufctl_t数组中的索引 */
- 04. unsigned int objnr = obj_to_index(cachep, slabp, objp);
- 05.
- 06.#if DEBUG
- 07. /* Verify that the slab belongs to the intended node */
- 08. WARN_ON(slabp->nodeid != nodeid);
- 09.
- 10. if (slab_bufctl(slabp)[objnr] + 1 <= SLAB_LIMIT + 1) {
- 11. printk(KERN_ERR "slab: double free detected in cache "
- 12. "'%s', objp %p\n", cachep->name, objp);
- 13. BUG();
- 14. }
- 15.#endif
- 16. /*这两步相当于静态链表的插入操作*/
- 17. /* 指向slab中原来的第一个空闲对象 */
- 18. slab_bufctl(slabp)[objnr] = slabp->free;
- 19. /* 释放的对象作为第一个空闲对象 */
- 20. slabp->free = objnr;
- 21. /* 已分配对象数减一 */
- 22. slabp->inuse--;
- 23.}
复制代码 辅助函数
view plaincopy to clipboard- 01./* 通过虚拟地址得到page,再通过page得到slab */
- 02.static inline struct slab *virt_to_slab(const void *obj)
- 03.{
- 04. struct page *page = virt_to_head_page(obj);
- 05. return page_get_slab(page);
- 06.}
复制代码 view plaincopy to clipboard- 01.static inline struct slab *page_get_slab(struct page *page)
- 02.{
- 03. BUG_ON(!PageSlab(page));
- 04. return (struct slab *)page->lru.prev;
- 05.}
-
复制代码 可见,用page->lru.prev得到slab,和创建slab时相呼应。
|
|