Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1071474
  • 博文数量: 277
  • 博客积分: 8313
  • 博客等级: 中将
  • 技术积分: 2976
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-22 11:25
文章分类

全部博文(277)

文章存档

2013年(17)

2012年(66)

2011年(104)

2010年(90)

我的朋友

分类: LINUX

2012-06-11 09:24:47

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

[cpp] view plaincopyprint?

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<gfporder个页面 

58.      objpslab首页面的虚拟地址 */  

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.      /* 设置pagecacheslab的映射 */  

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<gfporder个页面用于slab

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

[cpp] view plaincopyprint?

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 {/* 内置式slabobjpslab首页面的虚拟地址,加上着色偏移 

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.      /* 第一个对象的页内偏移,可见对于内置式slabcolouroff成员不仅包括着色区 

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

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

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<gfporder个)到slabcache的映射。这样,可以通过pagelru链表找到page所属的slabcacheslab_map_pages()实现

[cpp] view plaincopyprint?

1.     /*设置pagecacheslab的指针,这样就能知道页面所在的cacheslab 

2.         addrslab首页面虚拟地址*/  

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所在的cacheprev指向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]获得下一个空闲对象的索引并用它更新静态链表。

[cpp] view plaincopyprint?

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.  }  

 

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