Chinaunix首页 | 论坛 | 博客
  • 博客访问: 47002
  • 博文数量: 14
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 35
  • 用 户 组: 普通用户
  • 注册时间: 2018-11-19 18:25
文章分类

全部博文(14)

文章存档

2018年(14)

我的朋友

分类: LINUX

2018-11-19 19:45:42

在前文分析kmalloc时,首先从local cache中获取,没有空闲对象时再从shared local cache中获取,再从slab的三个链表中获取。
当链表中也没有空闲对象时,则需要调用cache_grow函数来创建slab了。实际上刚开始创建cache时里面是没有slab对象的,真正使用时才进行slab的创建。

下面分析一下cache_grow的实现。

点击(此处)折叠或打开

  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. /* cachep: 需要扩容的cache;  flags: 是否可以阻塞等; nodeie:对应的NUMA节点ID; 
  6.    objp: 页面虚拟地址,为NULL代表尚未申请到页面;不为空代表已经申请,可以直接创建slab */
  7. static int cache_grow(struct kmem_cache *cachep,
  8.         gfp_t flags, int nodeid, void *objp)
  9. {
  10.     struct slab *slabp;
  11.     size_t offset;
  12.     gfp_t local_flags;
  13.     struct kmem_list3 *l3;

  14.     /*
  15.      * Be lazy and only check for valid flags here, keeping it out of the
  16.      * critical path in kmem_cache_alloc().
  17.      */
  18.     BUG_ON(flags & GFP_SLAB_BUG_MASK);
  19.     local_flags = flags & (GFP_CONSTRAINT_MASK|GFP_RECLAIM_MASK);

  20.     /* Take the l3 list lock to change the colour_next on this node */
  21.     check_irq_off();
  22.     /* 根据NUMA节点ID获取slab三链 */
  23.     l3 = cachep->nodelists[nodeid];
  24.     spin_lock(&l3->list_lock);

  25.     /* Get colour for the slab, and cal the next value. */
  26.     /* 获取slab的着色偏移 */
  27.     offset = l3->colour_next;
  28.     /* 更新着色偏移,使不同slab的着色偏移不同 */
  29.     l3->colour_next++;
  30.     /* 不能超过着色区的大小,否则重新循环 */
  31.     if (l3->colour_next >= cachep->colour)
  32.         l3->colour_next = 0;
  33.     spin_unlock(&l3->list_lock);
  34.     /* 根据着色区个数*着色偏移,计算着色区的大小 */
  35.     offset *= cachep->colour_off;
  36.     /* 是否允许阻塞,否则开启硬中断 */
  37.     if (local_flags & __GFP_WAIT)
  38.         local_irq_enable();

  39.     /*
  40.      * The test for missing atomic flag is performed here, rather than
  41.      * the more obvious place, simply to reduce the critical path length
  42.      * in kmem_cache_alloc(). If a caller is seriously mis-behaving they
  43.      * will eventually be caught here (where it matters).
  44.      */
  45.     kmem_flagcheck(cachep, flags);

  46.     /*
  47.      * Get mem for the objs. Attempt to allocate a physical page from
  48.      * 'nodeid'.
  49.      */
  50.     /* 是否已经申请页面,如尚未申请,则先申请 */
  51.     /* 分配1<gfporder个页面,objp为slab首页面地址 */
  52.     if (!objp)
  53.         objp = kmem_getpages(cachep, local_flags, nodeid);
  54.     if (!objp)
  55.         goto failed;

  56.     /* Get slab management. */
  57.     /* 分配slab管理对象 */
  58.     slabp = alloc_slabmgmt(cachep, objp, offset,
  59.             local_flags & ~GFP_CONSTRAINT_MASK, nodeid);
  60.     if (!slabp)
  61.         goto opps1;
  62.     /* 设置page到cache slab的映射 */
  63.     slab_map_pages(cachep, slabp, objp);
  64.     /* 初始化slab中的对象 */
  65.     cache_init_objs(cachep, slabp);

  66.     if (local_flags & __GFP_WAIT)
  67.         local_irq_disable();
  68.     check_irq_off();
  69.     spin_lock(&l3->list_lock);

  70.     /* Make slab active. */
  71.     /* 将申请到的slab挂入free链表上 */
  72.     list_add_tail(&slabp->list, &(l3->slabs_free));
  73.     /* 增加计数 */
  74.     STATS_INC_GROWN(cachep);
  75.     /* 更新空闲对象数量 */
  76.     l3->free_objects += cachep->num;
  77.     spin_unlock(&l3->list_lock);
  78.     return 1;
  79. opps1:
  80.     kmem_freepages(cachep, objp);
  81. failed:
  82.     if (local_flags & __GFP_WAIT)
  83.         local_irq_disable();
  84.     return 0;
  85. }

点击(此处)折叠或打开

  1. /*
  2.  * Get the memory for a slab management obj.
  3.  * For a slab cache when the slab descriptor is off-slab, slab descriptors
  4.  * always come from malloc_sizes caches. The slab descriptor cannot
  5.  * come from the same cache which is getting created because,
  6.  * when we are searching for an appropriate cache for these
  7.  * descriptors in kmem_cache_create, we search through the malloc_sizes array.
  8.  * If we are creating a malloc_sizes cache here it would not be visible to
  9.  * kmem_find_general_cachep till the initialization is complete.
  10.  * Hence we cannot have slabp_cache same as the original cache.
  11.  */
  12. /* 申请slab管理对象 */
  13. static struct slab *alloc_slabmgmt(struct kmem_cache *cachep, void *objp,
  14.                  int colour_off, gfp_t local_flags,
  15.                  int nodeid)
  16. {
  17.     struct slab *slabp;
  18.     /* 是否为外置slab */
  19.     if (OFF_SLAB(cachep)) {
  20.         /* Slab management obj is off-slab. */
  21.         /* 外置slab,从slabp_cache中申请一个对象,slabp_cache是在初始化阶段被设置上的 */
  22.         slabp = kmem_cache_alloc_node(cachep->slabp_cache,
  23.                      local_flags, nodeid);
  24.         /*
  25.          * If the first object in the slab is leaked (it's allocated
  26.          * but no one has a reference to it), we want to make sure
  27.          * kmemleak does not treat the ->s_mem pointer as a reference
  28.          * to the object. Otherwise we will not report the leak.
  29.          */
  30.         kmemleak_scan_area(slabp, offsetof(struct slab, list),
  31.                  sizeof(struct list_head), local_flags);
  32.         if (!slabp)
  33.             return NULL;
  34.     } else {
  35.         /* 内置slab */
  36.         /* objp为slab的首页面的虚拟地址,根据着色偏移,计算slabp管理对象的地址 */
  37.         slabp = objp + colour_off;
  38.         /* slab_size是管理对象大小,包含了slab和kmem_bufctl,此处计算第一个slab对象的偏移量 */
  39.         colour_off += cachep->slab_size;
  40.     }
  41.     /* 新申请的slab对象,已用对象数为0 */
  42.     slabp->inuse = 0;
  43.     /* 第1个对象的页面偏移 */
  44.     /* 内置slab,则为着色区偏移+slab_size;外置slab,则为着色区偏移 */
  45.     slabp->colouroff = colour_off;
  46.     /* 第一个对象的虚拟地址 */
  47.     slabp->s_mem = objp + colour_off;
  48.     /* slab所属于的NUMA节点ID */
  49.     slabp->nodeid = nodeid;
  50.     /* free标示可用对象的数组下标,刚申请完,所以数组中所有成员可用,从0开始 */
  51.     slabp->free = 0;
  52.     return slabp;
  53. }
继续盗用两张图(http://blog.csdn.net/bullbat/article/details/7190105),展示一下slab的组织形式



继续分析一下slab_map_pages,该函数会建立slab/cache与page之间的映射,可以通过page的lru链表找到page所属的slab和cache。

点击(此处)折叠或打开

  1. /*
  2.  * Map pages beginning at addr to the given cache and slab. This is required
  3.  * for the slab allocator to be able to lookup the cache and slab of a
  4.  * virtual address for kfree, ksize, kmem_ptr_validate, and slab debugging.
  5.  */
  6. /* cache/slab地址,以及addr 虚拟页面的首地址 */
  7. static void slab_map_pages(struct kmem_cache *cache, struct slab *slab,
  8.              void *addr)
  9. {
  10.     int nr_pages;
  11.     struct page *page;
  12.     
  13.     page = virt_to_page(addr);

  14.     nr_pages = 1;
  15.     /* 如果不是大页面,则计算页面个数 */
  16.     if (likely(!PageCompound(page)))
  17.         nr_pages <<= cache->gfporder;

  18.     do {
  19.         /* 当page用于slab时,page的lru->next指向page所在cache, lru->prev指向page所在的slab */
  20.         /* 当page空闲时,lru负责将page串联在一起 */
  21.         page_set_cache(page, cache);
  22.         page_set_slab(page, slab);
  23.         page++;
  24.     } while (--nr_pages);
  25. }
如下,来自(http://blog.csdn.net/bullbat/article/details/7190105):


点击(此处)折叠或打开

  1. /
  2. static void cache_init_objs(struct kmem_cache *cachep,
  3.              struct slab *slabp)
  4. {
  5.     int i;

  6.     /* index_to_obj: slab->s_mem + cache->buffer_size * idx */
  7.     /* s_mem为第一个对象的起始地址,buffer_size为对象的大小,idx为索引 */
  8.     for (i = 0; i < cachep->num; i++) {
  9.         /* 遍历slab中的所有对象,先获取对象的地址 */
  10.         void *objp = index_to_obj(cachep, slabp, i);
  11. #if DEBUG
  12.         /* need to poison the objs? */
  13.         if (cachep->flags & SLAB_POISON)
  14.             poison_obj(cachep, objp, POISON_FREE);
  15.         if (cachep->flags & SLAB_STORE_USER)
  16.             *dbg_userword(cachep, objp) = NULL;

  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.         if (cachep->flags & SLAB_RED_ZONE) {
  29.             if (*dbg_redzone2(cachep, objp) != RED_INACTIVE)
  30.                 slab_error(cachep, "constructor overwrote the"
  31.                      " end of an object");
  32.             if (*dbg_redzone1(cachep, objp) != RED_INACTIVE)
  33.                 slab_error(cachep, "constructor overwrote the"
  34.                      " start of an object");
  35.         }
  36.         if ((cachep->buffer_size % PAGE_SIZE) == 0 &&
  37.              OFF_SLAB(cachep) && cachep->flags & SLAB_POISON)
  38.             kernel_map_pages(virt_to_page(objp),
  39.                      cachep->buffer_size / PAGE_SIZE, 0);
  40. #else
  41.         /* 如果cache指定了构造函数,则使用构造函数进行初始化 */
  42.         if (cachep->ctor)
  43.             cachep->ctor(objp);
  44. #endif
  45.         /* slab_bufctl:  (kmem_bufctl_t *) (slabp + 1) */
  46.         /* kmem_bufctl数组紧接着struct slab存放,其中每个成员的值都代表了下一个可用对象的索引 */
  47.         /* 建立静态数组的索引,其中slab->free指向了当前可用的空闲对象,然后slab_bufctl(slabp)[slab->free]指向了下一个可用对象的索引 */
  48.         slab_bufctl(slabp)[i] = i + 1;
  49.     }
  50.     /* 最后一个元素指向BUCTL_END,代表无法继续获取空闲对象 */
  51.     slab_bufctl(slabp)[i - 1] = BUFCTL_END;
  52. }









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