Chinaunix首页 | 论坛 | 博客
  • 博客访问: 848349
  • 博文数量: 26
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 382
  • 用 户 组: 普通用户
  • 注册时间: 2019-09-08 12:19
个人简介

关于个人介绍,既然你诚心诚意的看了 我就大发慈悲的告诉你 为了防止世界被破坏 为了维护世界的和平 贯彻爱与真实的邪恶 可爱又迷人的反派角色 我们是穿梭在银河的火箭队 白洞,白色的明天在等着我们

文章分类

分类: LINUX

2020-06-12 11:01:55

本文主要以内核Linux 4.19来简单说明slab分配器的大概流程。slab是Linux操作系统的一种内存分配机制,slab分配算法采用cache 存储内核对象。slab 缓存、从缓存中分配和释放对象然后销毁缓存的过程必须要定义一个 kmem_cache 对象,然后对其进行初始化这个特定的缓存包含 32 字节的对象(来自百度百科)。为了防止内存碎片, Linux在slab机制中明确的将伙伴子系统中的page 分成了多组内存大小不同的片段,如96, 192, 2^3,2^4...等,不同大小的片段属于不同的kmem_cache, 因此,当用户调用kmalloc申请内存的时候,只会从对应内存大小片段的kmem_cache中去分配。

首先如下是一个kmem_cache结构(Linux中存在多个这样的结构,每个这样的结构可以管理分配对应大小的内存片段),这里只列出了关键的几个成员。

点击(此处)折叠或打开

  1. struct kmem_cache {
  2.     struct array_cache __percpu *cpu_cache;  // 用于找到空闲内存的结点信息

  3.     unsigned int batchcount;        // 一次分配或者释放的大小为size的数量
  4.     unsigned int limit;             // 该kmem最大支持的size数量,也就是几个node的总和
  5.     unsigned int shared;            // 共享数量
  6.     unsigned int size; // 每个内存分配块的大小(这个size包含给用户内存、pad,kasan大小的总和
  7.     unsigned int num;             // 每个order的page能分配的最大size个数

  8.     unsigned int gfporder;     // 需要从order为rfporder的伙伴子系统申请page

  9.     gfp_t allocflags;    // 申请page的flag

  10.     size_t colour;  
  11.     unsigned int colour_off;
  12.     struct kmem_cache *freelist_cache; // 如果管理结构和slag不在一起,就会使用该结构
  13.     unsigned int freelist_size;     // 管理结构大小

  14.     const char *name;  // 名字
  15.     int object_size;  // 用户能在当前kmem_cache申请的最大内存
  16.     int align; // 对齐

  17. #ifdef CONFIG_KASAN
  18.     struct kasan_cache kasan_info;  // 指向kasan结构,主要用于检查内存越界使用的
  19. #endif

  20.     unsigned int useroffset;    /* Usercopy region offset */
  21.     unsigned int usersize;        /* Usercopy region size */

  22.     struct kmem_cache_node *node[MAX_NUMNODES];  // 记录mem node(numa)的具体信息
  23. }

kmem_cache_node结构如下(该结构表示在numa中的一个内存bank),
点击(此处)折叠或打开

  1. struct kmem_cache_node {
  2.     spinlock_t list_lock;

  3.     struct list_head slabs_partial;  // 用于存放没有完全使用的page
  4.     struct list_head slabs_full; // 用于存放完全使用了的page
  5.     struct list_head slabs_free; // 用于存放空闲的page,这里的page可能会被释放到伙伴子系统当中
  6.     unsigned long total_slabs;    /* length of all slab lists */
  7.     unsigned long free_slabs;    /* length of free slab list only */
  8.     unsigned long free_objects;  // 空闲的大小为size的个数
  9.     unsigned int free_limit; // 空闲个数的最大值
  10.     unsigned int colour_next;    /* Per-node cache coloring */
  11.     struct array_cache *shared;    // 共享内存结点
  12.     struct alien_cache **alien;    /* on other nodes */
  13.     unsigned long next_reap;    /* updated without locking */
  14.     int free_touched;        /* updated without locking */
  15. }

从上可以了解到,从伙伴子系统中分配出来的page会被挂到对应内存node的partial/full/free等三个链表。当page刚被申请出来时在free链表,如果该page被部分使用了,则在partial链表,如果全部被使用了,则在full链表。对于array_cache结构如下,该结构是用于分配内存的时候,能快速拿到希望的内存结点而使用的,

点击(此处)折叠或打开

  1. struct array_cache {
  2.     unsigned int avail;  // cache中可用的内存片段数量
  3.     unsigned int limit; // ac中最大可用缓存的片段数量
  4.     unsigned int batchcount; // ac一次性申请或者释放的片段数量
  5.     unsigned int touched;
  6.     void *entry[];// 指向不同内存片段的内存地址
  7. }

上述为kmem_cache的关键结构体,那么用图形表示为,

因此,之间的关系如图,page的lru会链接到mem_node的slabs_xxx链表上,而page的freelist用来记录该order的page的不同片段的起始地址,page->s_mem为page所代码的地址的起始地址。另外分配有些从kmem_cache的ac中分配,然后再是mem_node(ac只是mem_node的一个缓存而已)。

先看看kmem_cache的初始化,初始化栈如下,本章只会对几个函数做出说明,
点击(此处)折叠或打开

  1. start_kernel
  2.     mm_init
  3.         kmem_cache_init          // 初始化kmem_cache
  4.             create_boot_cache    // 创建启动kmem,用于为创建其他kmem_cache的时候分配结点内存
  5.             create_kmalloc_cache // 创建用于分配kmem_cache的结点kmem
  6.             create_kmalloc_caches // 创建其他大小的kmem_cache
  7.                 new_kmalloc_cache // 创建新的一个kmem_cache
  8.                     create_kmalloc_cache
  9.                         create_boot_cache
  10.                             __kmem_cache_create  // 初始化kmem_cache结构成员的默认值
  11.                                 kasan_cache_create // 预留Kasan的空间,即增加size大小
  12.                                 set_objfreelist_slab_cache // 设置管理page片段的管理结构位置
  13.                                 set_off_slab_cache // 设置 page片段管理结构不在slab上
  14.                                 set_on_slab_cache // 设置Page片段管理结构在slab上
  15.                                 setup_cpu_cache // 初始化ac,即array_cache结构
  16.                                     enable_cpucache

从上发现,在kmem_cache_init中,需要先调用create_boot_cache,这是为什么呢?这主要是因为后期在创建kmem_cache的时候也会申请内存,但是由于kmem_cache都还没有,又怎么能申请到内存呢,于是增加了一个boot_kmem_cache,这个结构是一个静态结构,不需要分配空间。这样,当初始化了这个结构后,后面去创建其他kmem_cache的时候,就能正常分配结点内存了。

点击(此处)折叠或打开

  1. void __init kmem_cache_init(void)
  2. {
  3.     int i;

  4.     kmem_cache = &kmem_cache_boot; // 准备初始化boot kmem cache

  5.     if (!IS_ENABLED(CONFIG_NUMA) || num_possible_nodes() == 1)
  6.         use_alien_caches = 0;

  7.     for (i = 0; i < NUM_INIT_LISTS; i++)
  8.         kmem_cache_node_init(&init_kmem_cache_node[i]);  // 初始化kmem node信息

  9.     if (!slab_max_order_set && totalram_pages > (32 << 20) >> PAGE_SHIFT)
  10.         slab_max_order = SLAB_MAX_ORDER_HI;

  11.     // 初始化 boot kmem_cache
  12.     create_boot_cache(kmem_cache, "kmem_cache",
  13.                       offsetof(struct kmem_cache, node) +
  14.                       nr_node_ids * sizeof(struct kmem_cache_node *),
  15.                       SLAB_HWCACHE_ALIGN, 0, 0);
  16.     list_add(&kmem_cache->list, &slab_caches); // 添加到slag_caches全局链表
  17.     memcg_link_cache(kmem_cache);
  18.     slab_state = PARTIAL; // slab_stae设置为PARTIAL,这影响array_cache和node初始化
  19.     
  20.     //初始化用于申请node的结点
  21.     kmalloc_caches[INDEX_NODE] = create_kmalloc_cache(
  22.                 kmalloc_info[INDEX_NODE].name,
  23.                 kmalloc_size(INDEX_NODE), ARCH_KMALLOC_FLAGS,
  24.                 0, kmalloc_size(INDEX_NODE));
  25.     slab_state = PARTIAL_NODE;  // 设置slab_state为PARTAL_NODE, setup_cpu_cache会用到
  26.     setup_kmalloc_cache_index_table();

  27.     slab_early_init = 0;

  28.     {  // 将boot kmem cache的node数据内容和INDEX_NODE的node数据置换
  29.         int nid;

  30.         for_each_online_node(nid) {
  31.             init_list(kmem_cache, &init_kmem_cache_node[CACHE_CACHE + nid], nid);

  32.             init_list(kmalloc_caches[INDEX_NODE],
  33.                      &init_kmem_cache_node[SIZE_NODE + nid], nid);
  34.         }
  35.     }
  36. // 创建其他大小的kmem_cache,实现就是一个for循环,调用__kmem_cache_create的过程
  37. create_kmalloc_caches(ARCH_KMALLOC_FLAGS);
  38. }

其他的函数没什么说的, 下面说说 __kmem_cache_create函数,该函数用来初始化kmem_cache,实现如下

点击(此处)折叠或打开

  1. int __kmem_cache_create(struct kmem_cache *cachep, slab_flags_t flags)
  2. {
  3.     size_t ralign = BYTES_PER_WORD;
  4.     gfp_t gfp;
  5.     int err;
  6.     unsigned int size = cachep->size;

  7.     size = ALIGN(size, BYTES_PER_WORD); // 对齐
  8.     if (flags & SLAB_RED_ZONE) {  // kasan对齐相关
  9.         ralign = REDZONE_ALIGN;
  10.         size = ALIGN(size, REDZONE_ALIGN);
  11.     }
  12.     if (ralign < cachep->align) {
  13.         ralign = cachep->align;
  14.     }

  15.     if (ralign > __alignof__(unsigned long long))
  16.         flags &= ~(SLAB_RED_ZONE | SLAB_STORE_USER);

  17.     cachep->align = ralign; //赋值对齐值
  18.     cachep->colour_off = cache_line_size();

  19.     if (cachep->colour_off < cachep->align)
  20.         cachep->colour_off = cachep->align;

  21.     if (slab_is_available()) // 申请标记
  22.         gfp = GFP_KERNEL;
  23.     else
  24.         gfp = GFP_NOWAIT;

  25.     kasan_cache_create(cachep, &size, &flags); // 如果支持kasan,则为kasan在用户申请内存中,预留空间

  26.     size = ALIGN(size, cachep->align);

  27.     if (FREELIST_BYTE_INDEX && size < SLAB_OBJ_MIN_SIZE)
  28.         size = ALIGN(SLAB_OBJ_MIN_SIZE, cachep->align);
  29.  
  30.     // 这里需要注意,freelist用于管理对应order的page中分出来的大小为size的每个片段索引号,objfreelist, off slab, on slab三者只用其中一个,优先考虑objfreelist,然后是off slab,最后是on slab
  31.     if (set_objfreelist_slab_cache(cachep, size, flags)) {
  32.         flags |= CFLGS_OBJFREELIST_SLAB;
  33.         goto done;
  34.     }

  35.     if (set_off_slab_cache(cachep, size, flags)) {
  36.         flags |= CFLGS_OFF_SLAB;
  37.         goto done;
  38.     }

  39.     if (set_on_slab_cache(cachep, size, flags))
  40.         goto done;

  41.     return -E2BIG;

  42. done:
  43.     cachep->freelist_size = cachep->num * sizeof(freelist_idx_t); // freelist大小
  44.     cachep->flags = flags;
  45.     cachep->allocflags = __GFP_COMP;
  46.     if (flags & SLAB_CACHE_DMA)
  47.         cachep->allocflags |= GFP_DMA;
  48.     if (flags & SLAB_CACHE_DMA32)
  49.         cachep->allocflags |= GFP_DMA32;
  50.     if (flags & SLAB_RECLAIM_ACCOUNT)
  51.         cachep->allocflags |= __GFP_RECLAIMABLE;
  52.     cachep->size = size;
  53.     cachep->reciprocal_buffer_size = reciprocal_value(size);

  54.     if (OFF_SLAB(cachep)) {
  55.         cachep->freelist_cache =
  56.             kmalloc_slab(cachep->freelist_size, 0u);
  57.     }

  58.     err = setup_cpu_cache(cachep, gfp); // 初始化 cpu cache和mem node
  59.     if (err) {
  60.         __kmem_cache_release(cachep);
  61.         return err;
  62.     }

  63.     return 0;
  64. }

通过上面函数,我们大概知道用户调用kmalloc申请一个内存的时候,内存的分布

如上图,user_size才是用户申请的时候填写的数据大小,后面都是为了检测内存越界而预留的空间。

点击(此处)折叠或打开

  1. static int __ref setup_cpu_cache(struct kmem_cache *cachep, gfp_t gfp)
  2. {
  3.     if (slab_state >= FULL) // 刚开始 slab_state状态是DOWN,只有最后都成功了会是FULL
  4.         return enable_cpucache(cachep, gfp);

  5.     cachep->cpu_cache = alloc_kmem_cache_cpus(cachep, 1, 1);
  6.     if (!cachep->cpu_cache)
  7.         return 1;

  8.     if (slab_state == DOWN) {  // 安装boot kmem的node信息
  9.         /* Creation of first cache (kmem_cache). */
  10.         set_up_node(kmem_cache, CACHE_CACHE);
  11.     } else if (slab_state == PARTIAL) {
  12.         /* For kmem_cache_node */
  13.         set_up_node(cachep, SIZE_NODE); // 安装size node的node信息
  14.     } else {
  15.         int node;

  16.         for_each_online_node(node) { // 为每个mem node申请内存结点
  17.             cachep->node[node] = kmalloc_node(
  18.                 sizeof(struct kmem_cache_node), gfp, node);
  19.             BUG_ON(!cachep->node[node]);
  20.             kmem_cache_node_init(cachep->node[node]);
  21.         }
  22.     }
  23.     // 初始化ac结点
  24.     cachep->node[numa_mem_id()]->next_reap =
  25.             jiffies + REAPTIMEOUT_NODE +
  26.             ((unsigned long)cachep) % REAPTIMEOUT_NODE;

  27.     cpu_cache_get(cachep)->avail = 0;
  28.     cpu_cache_get(cachep)->limit = BOOT_CPUCACHE_ENTRIES;
  29.     cpu_cache_get(cachep)->batchcount = 1;
  30.     cpu_cache_get(cachep)->touched = 0;
  31.     cachep->batchcount = 1;
  32.     cachep->limit = BOOT_CPUCACHE_ENTRIES;
  33.     return 0;
  34. }

从上面知道,初始化的时候,ac的limit都是1,那么什么时候会改变limit的值呢?栈如下,
点击(此处)折叠或打开

  1. start_kernel
  2.     kmem_cache_init_late
  3.         enable_cpucache // 这里会调整ac等的limit值

内存申请函数kmalloc,申请规则:kmalloc会先从对应kmem_cache的ac缓存中去获取内存片段,如果无法从ac中获取到内存片段,则尝试从node的shared中分配,如果依然无法分配则考虑从mem node的链表上去获取page,当然如果链表上也没有了,就只有从最慢的的伙伴子系统中去分配了,函数调用栈如下,

点击(此处)折叠或打开

  1. kmalloc  
  2.     kmalloc_large // 用户传入的size大于KMALLOC_MAX_CACHE_SIZE的时候,用kmalloc_order,实际就是从伙伴子系统中分配内存
  3.         kmalloc_order
  4.             alloc_pages
  5.     __kmalloc
  6.         __do_kmalloc
  7.             kmalloc_slab // 通过用户传入的size,查找需要从哪个kmem_cache中分配
  8.             slab_alloc
  9.                 __do_cache_alloc
  10.                     ____cache_alloc // 从kmem_cache中分配内存
  11.             kasan_kmalloc // 将内存object_size没有用完的部分,填充kasan red pading

下面重点讲解 ___cache_alloc函数,该函数完成内存从kmem_cache中的分配,实现如下
点击(此处)折叠或打开

  1. static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags)
  2. {
  3.     void *objp;
  4.     struct array_cache *ac;

  5.     check_irq_off();

  6.     ac = cpu_cache_get(cachep); // 获取到当前kmem_cache的arrary_cache成员
  7.     if (likely(ac->avail)) { // 判断ac中是否还有未分配的内存片段,有就直接赋值分配。goto out
  8.         ac->touched = 1;
  9.         objp = ac->entry[--ac->avail];

  10.         STATS_INC_ALLOCHIT(cachep);
  11.         goto out;
  12.     }
  13.     // 因为ac中没有可用于分配的内存片段,则需要从slabs_partial或者slabs_free链表中去要
  14.     STATS_INC_ALLOCMISS(cachep)
  15.     objp = cache_alloc_refill(cachep, flags);

  16.     ac = cpu_cache_get(cachep);

  17. out:
  18.     if (objp) // 如果分配内存成功,将ac中对该内存信息,进行清除,以免再次被分配
  19.         kmemleak_erase(&ac->entry[ac->avail]);
  20.     return objp;
  21. }

从node链表slabs_partial、slabs_free中去获取内存片段的函数实现如下,
点击(此处)折叠或打开

  1. static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags)
  2. {
  3.     int batchcount;
  4.     struct kmem_cache_node *n;
  5.     struct array_cache *ac, *shared;
  6.     int node;
  7.     void *list = NULL;
  8.     struct page *page;

  9.     check_irq_off();
  10.     node = numa_mem_id();

  11.     ac = cpu_cache_get(cachep); // 获取ac
  12.     batchcount = ac->batchcount; // 得到一次性要从node中申请的片段个数
  13.     if (!ac->touched && batchcount > BATCHREFILL_LIMIT) {
  14.         batchcount = BATCHREFILL_LIMIT;
  15.     }
  16.     n = get_node(cachep, node); // 得到cache对应的Node结构

  17.     BUG_ON(ac->avail > 0 || !n);
  18.     shared = READ_ONCE(n->shared); // 如果存在没有共享内存片段,并且结点从也没有内存片段,则需要跳转到direct_grow去向伙伴子系统进货
  19.     if (!n->free_objects && (!shared || !shared->avail))
  20.         goto direct_grow;

  21.     spin_lock(&n->list_lock);
  22.     shared = READ_ONCE(n->shared);

  23.     // 发现有共享内存片段,则优先从共享中申请batchcount个片段
  24.     if (shared && transfer_objects(ac, shared, batchcount)) {
  25.         shared->touched = 1;
  26.         goto alloc_done;
  27.     }

  28.     while (batchcount > 0) { // 还有片段未申请完,则调用get_first_slab从node的链表上获取
  29.         /* Get slab alloc is to come from. */
  30.         page = get_first_slab(n, false);
  31.         if (!page)
  32.             goto must_grow;

  33.         check_spinlock_acquired(cachep);

  34.         batchcount = alloc_block(cachep, ac, page, batchcount);
  35.         fixup_slab_list(cachep, n, page, &list); // 因为page中的片段被使用,则需要调整page所在的链表
  36.     }

  37. must_grow:
  38.     n->free_objects -= ac->avail;
  39. alloc_done:
  40.     spin_unlock(&n->list_lock);
  41.     fixup_objfreelist_debug(cachep, &list);

  42. direct_grow: // 没有足够的内存片段,则向伙伴子系统进货
  43.     if (unlikely(!ac->avail)) {
  44.         /* Check if we can use obj in pfmemalloc slab */
  45.         if (sk_memalloc_socks()) {
  46.             void *obj = cache_alloc_pfmemalloc(cachep, n, flags);

  47.             if (obj)
  48.                 return obj;
  49.         }

  50.         page = cache_grow_begin(cachep, gfp_exact_node(flags), node); // 从伙伴子系统申请一个order的page

  51.         /*
  52.          * cache_grow_begin() can reenable interrupts,
  53.          * then ac could change.
  54.          */
  55.         ac = cpu_cache_get(cachep);
  56.         if (!ac->avail && page)
  57.             alloc_block(cachep, ac, page, batchcount); // 释放到ac中缓存起来
  58.         cache_grow_end(cachep, page);

  59.         if (!ac->avail)
  60.             return NULL;
  61.     }
  62.     ac->touched = 1;

  63.     return ac->entry[--ac->avail]; // 返回第一个片段
  64. }

get_first_slab实现如下,
点击(此处)折叠或打开

  1. static struct page *get_first_slab(struct kmem_cache_node *n, bool pfmemalloc)
  2. {
  3.     struct page *page;

  4.     assert_spin_locked(&n->list_lock);// 优先从slabs_partial中去获取page
  5.     page = list_first_entry_or_null(&n->slabs_partial, struct page, lru);
  6.     if (!page) {
  7.         n->free_touched = 1;
  8.         page = list_first_entry_or_null(&n->slabs_free, struct page,
  9.                         lru);
  10.         if (page)
  11.             n->free_slabs--;
  12.     }

  13.     if (sk_memalloc_socks())
  14.         page = get_valid_first_slab(n, page, pfmemalloc);

  15.     return page;
  16. }

fixup_slab_list实现如下,
点击(此处)折叠或打开

  1. static inline void fixup_slab_list(struct kmem_cache *cachep,
  2.                 struct kmem_cache_node *n, struct page *page,
  3.                 void **list)
  4. {
  5.     list_del(&page->lru);
  6.     if (page->active == cachep->num) {
  7.         list_add(&page->lru, &n->slabs_full);
  8.         if (OBJFREELIST_SLAB(cachep)) {
  9.             page->freelist = NULL;
  10.         }
  11.     } else
  12.         list_add(&page->lru, &n->slabs_partial);
  13. }

内存释放函数kfree, 释放规则:kfree会优先将内存片段释放到kmem_cache的ac缓存中,如果ac缓存满了,则需要将一定数量的内存片段释放到mem node的链表上,如果mem node的链表也超过了限制,就将page释放到伙伴子系统当中。为什么要设置限制?就是防止slab占用过多的内存,而导致内核系统中其他区间可用内存不足。函数调用栈如下,
点击(此处)折叠或打开

  1. kfree
  2.     virt_to_cache // 通过虚拟地址获取对应的kmem_cache结构
  3.     __cache_free
  4.         kasan_slab_free // 释放kasan
  5.         ___cache_free // 释放内存片段到kmem_cache

___cache_free实现如下,
点击(此处)折叠或打开

  1. void ___cache_free(struct kmem_cache *cachep, void *objp,
  2.         unsigned long caller)
  3. {
  4.     struct array_cache *ac = cpu_cache_get(cachep); // 获取到ac

  5.     check_irq_off();
  6.     kmemleak_free_recursive(objp, cachep->flags);
  7.     objp = cache_free_debugcheck(cachep, objp, caller);

  8.     if (nr_online_nodes > 1 && cache_free_alien(cachep, objp))
  9.         return;

  10.     if (ac->avail < ac->limit) { // 如果ac中缓存的数量没有超过限制,就释放到ac中缓存起来
  11.         STATS_INC_FREEHIT(cachep);
  12.     } else { // ac中的缓存超过限制,则释放到node对应的链表上
  13.         STATS_INC_FREEMISS(cachep);
  14.         cache_flusharray(cachep, ac);
  15.     }

  16.     if (sk_memalloc_socks()) {
  17.         struct page *page = virt_to_head_page(objp);

  18.         if (unlikely(PageSlabPfmemalloc(page))) {
  19.             cache_free_pfmemalloc(cachep, page, objp);
  20.             return;
  21.         }
  22.     }

  23.     ac->entry[ac->avail++] = objp;
  24. }

cache_flusharray释放一个batchcount的内存片段到node的链表上,实现如下
点击(此处)折叠或打开

  1. static void cache_flusharray(struct kmem_cache *cachep, struct array_cache *ac)
  2. {
  3.     int batchcount;
  4.     struct kmem_cache_node *n;
  5.     int node = numa_mem_id();
  6.     LIST_HEAD(list);

  7.     batchcount = ac->batchcount;

  8.     check_irq_off();
  9.     n = get_node(cachep, node);
  10.     spin_lock(&n->list_lock);
  11.     if (n->shared) { // 存在共享,则优先释放到node的共享结点上
  12.         struct array_cache *shared_array = n->shared;
  13.         int max = shared_array->limit - shared_array->avail;
  14.         if (max) {
  15.             if (batchcount > max)
  16.                 batchcount = max;
  17.             memcpy(&(shared_array->entry[shared_array->avail]),
  18.              ac->entry, sizeof(void *) * batchcount);
  19.             shared_array->avail += batchcount;
  20.             goto free_done;
  21.         }
  22.     }

  23.     free_block(cachep, ac->entry, batchcount, node, &list); // 释放ac到node的链表上,如果存在node中也超出了限制,则把free链表上的page挂到list指向的链表上
  24. free_done:
  25.     spin_unlock(&n->list_lock);
  26.     slabs_destroy(cachep, &list); // 将list上面挂的page释放到伙伴子系统
  27.     ac->avail -= batchcount; // 调整ac缓存
  28.     memmove(ac->entry, &(ac->entry[batchcount]), sizeof(void *)*ac->avail);
  29. }

free_block实现,
点击(此处)折叠或打开

  1. static void free_block(struct kmem_cache *cachep, void **objpp,
  2.             int nr_objects, int node, struct list_head *list)
  3. {
  4.     int i;
  5.     struct kmem_cache_node *n = get_node(cachep, node);
  6.     struct page *page;

  7.     n->free_objects += nr_objects;
  8.     // 依次释放ac中的每个内存片段
  9.     for (i = 0; i < nr_objects; i++) {
  10.         void *objp;
  11.         struct page *page;

  12.         objp = objpp[i];

  13.         page = virt_to_head_page(objp);
  14.         list_del(&page->lru);
  15.         check_spinlock_acquired_node(cachep, node);
  16.         slab_put_obj(cachep, page, objp);
  17.         STATS_DEC_ACTIVE(cachep);
  18.         if (page->active == 0) { // 如果active为0表示,该page的所有片段都已经被释放,空闲
  19.             list_add(&page->lru, &n->slabs_free);
  20.             n->free_slabs++;
  21.         } else {
  22.             list_add_tail(&page->lru, &n->slabs_partial);
  23.         }
  24.     }
  25.    // 这里如果free_objects数量超出了free_limit限制,则释放slabs_free上面的page
  26.     while (n->free_objects > n->free_limit && !list_empty(&n->slabs_free)) {
  27.         n->free_objects -= cachep->num;

  28.         page = list_last_entry(&n->slabs_free, struct page, lru);
  29.         list_move(&page->lru, list);
  30.         n->free_slabs--;
  31.         n->total_slabs--;
  32.     }
  33. }

什么是kasan? kasan是Linux内核引入的一种专门检测slab内存越界问题的,通过给内存打上特定的pad来实现,这个操作比较消耗内存。

阅读(221) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~