感谢支持
我们一直在努力

Linux内存管理之slab机制(创建slab)

Linux内核中创建slab主要由函数cache_grow()实现,从slab的创建中我们可以完整地看到slab与对象、页面的组织方式。


  1. /* 

  2.  * Grow (by 1) the number of slabs within a cache.  This is called by 

  3.  * kmem_cache_alloc() when there are no active objs left in a cache. 

  4.  */  

  5.  /*使用一个或多个页面创建一个空slab。 

  6. objp:页面虚拟地址,为空表示还未申请内存页,不为空 

  7. ,说明已申请内存页,可直接用来创建slab*/  

  8. static int cache_grow(struct kmem_cache *cachep,  

  9.         gfp_t flags, int nodeid, void *objp)  

  10. {  

  11.     struct slab *slabp;  

  12.     size_t offset;  

  13.     gfp_t local_flags;  

  14.     struct kmem_list3 *l3;  

  15.   

  16.     /* 

  17.      * Be lazy and only check for valid flags here,  keeping it out of the 

  18.      * critical path in kmem_cache_alloc(). 

  19.      */  

  20.     BUG_ON(flags & GFP_SLAB_BUG_MASK);  

  21.     local_flags = flags & (GFP_CONSTRAINT_MASK|GFP_RECLAIM_MASK);  

  22.   

  23.     /* Take the l3 list lock to change the colour_next on this node */  

  24.     check_irq_off();  

  25.      /* 获得本内存节点的slab三链 */  

  26.     l3 = cachep->nodelists[nodeid];  

  27.     spin_lock(&l3->list_lock);  

  28.   

  29.     /* Get colour for the slab, and cal the next value. */  

  30.     /* 获得本slab的着色区偏移 */  

  31.     offset = l3->colour_next;  

  32.      /* 更新着色区偏移,使不同slab的着色偏移不同 */  

  33.     l3->colour_next++;  

  34.     /* 不能超过着色区的总大小,如果超过了,重置为0。这就是前面分析过的着色循环问题 

  35.     。事实上,如果slab中浪费的空间很少,那么很快就会循环一次。*/  

  36.     if (l3->colour_next >= cachep->colour)  

  37.         l3->colour_next = 0;  

  38.     spin_unlock(&l3->list_lock);  

  39.     /* 将着色单位区间的个数转换为着色区大小 */  

  40.     offset *= cachep->colour_off;  

  41.   

  42.     if (local_flags & __GFP_WAIT)  

  43.         local_irq_enable();  

  44.   

  45.     /* 

  46.      * The test for missing atomic flag is performed here, rather than 

  47.      * the more obvious place, simply to reduce the critical path length 

  48.      * in kmem_cache_alloc(). If a caller is seriously mis-behaving they 

  49.      * will eventually be caught here (where it matters). 

  50.      */  

  51.     kmem_flagcheck(cachep, flags);  

  52.   

  53.     /* 

  54.      * Get mem for the objs.  Attempt to allocate a physical page from 

  55.      * ‘nodeid’. 

  56.      */  

  57.     if (!objp)/* 还未分配页面,从本内存节点分配1<<cachep->gfporder个页面 

  58.     ,objp为slab首页面的虚拟地址 */  

  59.         objp = kmem_getpages(cachep, local_flags, nodeid);  

  60.     if (!objp)  

  61.         goto failed;  

  62.   

  63.     /* Get slab management. */  

  64.      /* 分配slab管理对象 */  

  65.     slabp = alloc_slabmgmt(cachep, objp, offset,  

  66.             local_flags & ~GFP_CONSTRAINT_MASK, nodeid);  

  67.     if (!slabp)  

  68.         goto opps1;  

  69.     /* 设置page到cache、slab的映射 */  

  70.        slab_map_pages(cachep, slabp, objp);  

  71.   

  72.        /* 初始化slab中的对象 */  

  73.     cache_init_objs(cachep, slabp);  

  74.   

  75.     if (local_flags & __GFP_WAIT)  

  76.         local_irq_disable();  

  77.     check_irq_off();  

  78.     spin_lock(&l3->list_lock);  

  79.   

  80.     /* Make slab active. */  

  81.     list_add_tail(&slabp->list, &(l3->slabs_free));  

  82.     /* 更新本cache增长计数 */  

  83.     STATS_INC_GROWN(cachep);  

  84.     /* 更新slab链表中空闲对象计数 */  

  85.     l3->free_objects += cachep->num;  

  86.     spin_unlock(&l3->list_lock);  

  87.     return 1;  

  88. opps1:  

  89.     kmem_freepages(cachep, objp);  

  90. failed:  

  91.     if (local_flags & __GFP_WAIT)  

  92.         local_irq_disable();  

  93.     return 0;  

  94. }  

执行流程:


1,从cache结构中获得并计算着色区偏移量;


2,从伙伴系统中获得1<<cachep->gfporder个页面用于slab


3,初始化slab中相关变量,如果是外置式slab需要从新申请slab管理区的空间,由函数alloc_slabmgmt()实现。


  1. /*分配slab管理对象*/  

  2. static struct slab *alloc_slabmgmt(struct kmem_cache *cachep, void *objp,  

  3.                    int colour_off, gfp_t local_flags,  

  4.                    int nodeid)  

  5. {  

  6.     struct slab *slabp;  

  7.   

  8.     if (OFF_SLAB(cachep)) {  

  9.         /* Slab management obj is off-slab. */  

  10.         /* 外置式slab。从general slab cache中分配一个管理对象, 

  11.         slabp_cache指向保存有struct slab对象的general slab cache。 

  12.         slab初始化阶段general slab cache可能还未创建,slabp_cache指针为空 

  13.         ,故初始化阶段创建的slab均为内置式slab。*/  

  14.         slabp = kmem_cache_alloc_node(cachep->slabp_cache,  

  15.                           local_flags, nodeid);  

  16.         /* 

  17.          * If the first object in the slab is leaked (it’s allocated 

  18.          * but no one has a reference to it), we want to make sure 

  19.          * kmemleak does not treat the ->s_mem pointer as a reference 

  20.          * to the object. Otherwise we will not report the leak. 

  21.          *//* 对第一个对象做检查 */  

  22.         kmemleak_scan_area(slabp, offsetof(struct slab, list),  

  23.                    sizeof(struct list_head), local_flags);  

  24.         if (!slabp)  

  25.             return NULL;  

  26.     } else {/* 内置式slab。objp为slab首页面的虚拟地址,加上着色偏移 

  27.     ,得到slab管理对象的虚拟地址 */  

  28.         slabp = objp + colour_off;  

  29.         /* 计算slab中第一个对象的页内偏移,slab_size保存slab管理对象的大小 

  30.         ,包含struct slab对象和kmem_bufctl_t数组 */  

  31.         colour_off += cachep->slab_size;  

  32.     } /* 在用(已分配)对象数为0 */  

  33.     slabp->inuse = 0;  

  34.     /* 第一个对象的页内偏移,可见对于内置式slab,colouroff成员不仅包括着色区 

  35.     ,还包括管理对象占用的空间 

  36.     ,外置式slab,colouroff成员只包括着色区。*/  

  37.     slabp->colouroff = colour_off;  

  38.     /* 第一个对象的虚拟地址 */  

  39.     slabp->s_mem = objp + colour_off;  

  40.     /* 内存节点ID */  

  41.     slabp->nodeid = nodeid;  

  42.     /* 第一个空闲对象索引为0,即kmem_bufctl_t数组的第一个元素 */  

  43.     slabp->free = 0;  

  44.     return slabp;  

  45. }  

通过初始化,我们画出下面图像。





4,设置slab中页面(1<<cachep->gfporder个)到slabcache的映射。这样,可以通过pagelru链表找到page所属的slabcacheslab_map_pages()实现


  1. /*设置page到cache、slab的指针,这样就能知道页面所在的cache、slab 

  2.     addr:slab首页面虚拟地址*/  

  3. static void slab_map_pages(struct kmem_cache *cache, struct slab *slab,  

  4.                void *addr)  

  5. {  

  6.     int nr_pages;  

  7.     struct page *page;  

  8.     /* 获得slab首页面*/  

  9.     page = virt_to_page(addr);  

  10.   

  11.     nr_pages = 1;  

  12.      /* 如果不是大页面(关于大页面请参阅相关文档) 

  13.      ,计算页面的个数 */  

  14.     if (likely(!PageCompound(page)))  

  15.         nr_pages <<= cache->gfporder;  

  16.   

  17.     do {  

  18.         /* struct page结构中的lru根据页面的用途有不同的含义 

  19.         ,当页面空闲或用于高速缓存时, 

  20.         lru成员用于构造双向链表将page串联起来,而当page用于slab时, 

  21.         next指向page所在的cache,prev指向page所在的slab */  

  22.         page_set_cache(page, cache);  

  23.         page_set_slab(page, slab);  

  24.         page++;  

  25.     } while (–nr_pages);  

  26. }  

代码实现结果如下图



5,初始化slabkmem_bufctl_t[]数组,其中kmem_bufctl_t[]数组为一个静态链表,指定了slab对象(obj)的访问顺序。即kmem_bufctl_t[]中存放的是下一个访问的obj。在后面分析中slab_get_obj()函数从slab中提取一个空闲对象,他通过index_to_obj()函数找到空闲对象在kmem_bufctl_t[]数组中的下标,然后通过slab_bufctl(slabp)[slabp->free]获得下一个空闲对象的索引并用它更新静态链表。


  1. /*初始化slab中的对象,主要是通过kmem_bufctl_t数组将对象串联起来*/  

  2. static void cache_init_objs(struct kmem_cache *cachep,  

  3.                 struct slab *slabp)  

  4. {  

  5.     int i;  

  6.     /* 逐一初始化slab中的对象 */  

  7.     for (i = 0; i < cachep->num; i++) {  

  8.          /* 获得slab中第i个对象 */  

  9.         void *objp = index_to_obj(cachep, slabp, i);  

  10. #if DEBUG   

  11.         /* need to poison the objs? */  

  12.         if (cachep->flags & SLAB_POISON)  

  13.             poison_obj(cachep, objp, POISON_FREE);  

  14.         if (cachep->flags & SLAB_STORE_USER)  

  15.             *dbg_userword(cachep, objp) = NULL;  

  16.   

  17.         if (cachep->flags & SLAB_RED_ZONE) {  

  18.             *dbg_redzone1(cachep, objp) = RED_INACTIVE;  

  19.             *dbg_redzone2(cachep, objp) = RED_INACTIVE;  

  20.         }  

  21.         /* 

  22.          * Constructors are not allowed to allocate memory from the same 

  23.          * cache which they are a constructor for.  Otherwise, deadlock. 

  24.          * They must also be threaded. 

  25.          */  

  26.         if (cachep->ctor && !(cachep->flags & SLAB_POISON))  

  27.             cachep->ctor(objp + obj_offset(cachep));  

  28.   

  29.         if (cachep->flags & SLAB_RED_ZONE) {  

  30.             if (*dbg_redzone2(cachep, objp) != RED_INACTIVE)  

  31.                 slab_error(cachep, “constructor overwrote the”  

  32.                        ” end of an object”);  

  33.             if (*dbg_redzone1(cachep, objp) != RED_INACTIVE)  

  34.                 slab_error(cachep, “constructor overwrote the”  

  35.                        ” start of an object”);  

  36.         }  

  37.         if ((cachep->buffer_size % PAGE_SIZE) == 0 &&  

  38.                 OFF_SLAB(cachep) && cachep->flags & SLAB_POISON)  

  39.             kernel_map_pages(virt_to_page(objp),  

  40.                      cachep->buffer_size / PAGE_SIZE, 0);  

  41. #else   

  42.         /* 调用此对象的构造函数 */  

  43.         if (cachep->ctor)  

  44.             cachep->ctor(objp);  

  45. #endif /* 初始时所有对象都是空闲的,只需按照数组顺序串起来即可 */   

  46.         /*相当于静态索引指针*/  

  47.         slab_bufctl(slabp)[i] = i + 1;  

  48.     }  

  49.     /* 最后一个指向BUFCTL_END */  

  50.     slab_bufctl(slabp)[i – 1] = BUFCTL_END;  

  51. }  

赞(0) 打赏
转载请注明出处:服务器评测 » Linux内存管理之slab机制(创建slab)
分享到: 更多 (0)

听说打赏我的人,都进福布斯排行榜啦!

支付宝扫一扫打赏

微信扫一扫打赏