免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1526 | 回复: 1
打印 上一主题 下一主题

Linux内存管理之slab机制(创建slab) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2012-01-10 15:15 |只看该作者 |倒序浏览
Linux内存管理之slab机制(创建slab)






Linux内核中创建slab主要由函数cache_grow()实现,从slab的创建中我们可以完整地看到slab与对象、页面的组织方式。
view plaincopy to clipboardprint?
  1. 01./*
  2. 02. * Grow (by 1) the number of slabs within a cache.  This is called by
  3. 03. * kmem_cache_alloc() when there are no active objs left in a cache.
  4. 04. */  
  5. 05. /*使用一个或多个页面创建一个空slab。
  6. 06.objp:页面虚拟地址,为空表示还未申请内存页,不为空
  7. 07.,说明已申请内存页,可直接用来创建slab*/  
  8. 08.static int cache_grow(struct kmem_cache *cachep,  
  9. 09.        gfp_t flags, int nodeid, void *objp)  
  10. 10.{  
  11. 11.    struct slab *slabp;  
  12. 12.    size_t offset;  
  13. 13.    gfp_t local_flags;  
  14. 14.    struct kmem_list3 *l3;  
  15. 15.  
  16. 16.    /*
  17. 17.     * Be lazy and only check for valid flags here,  keeping it out of the
  18. 18.     * critical path in kmem_cache_alloc().
  19. 19.     */  
  20. 20.    BUG_ON(flags & GFP_SLAB_BUG_MASK);  
  21. 21.    local_flags = flags & (GFP_CONSTRAINT_MASK|GFP_RECLAIM_MASK);  
  22. 22.  
  23. 23.    /* Take the l3 list lock to change the colour_next on this node */  
  24. 24.    check_irq_off();  
  25. 25.     /* 获得本内存节点的slab三链 */  
  26. 26.    l3 = cachep->nodelists[nodeid];  
  27. 27.    spin_lock(&l3->list_lock);  
  28. 28.  
  29. 29.    /* Get colour for the slab, and cal the next value. */  
  30. 30.    /* 获得本slab的着色区偏移 */  
  31. 31.    offset = l3->colour_next;  
  32. 32.     /* 更新着色区偏移,使不同slab的着色偏移不同 */  
  33. 33.    l3->colour_next++;  
  34. 34.    /* 不能超过着色区的总大小,如果超过了,重置为0。这就是前面分析过的着色循环问题
  35. 35.    。事实上,如果slab中浪费的空间很少,那么很快就会循环一次。*/  
  36. 36.    if (l3->colour_next >= cachep->colour)  
  37. 37.        l3->colour_next = 0;  
  38. 38.    spin_unlock(&l3->list_lock);  
  39. 39.    /* 将着色单位区间的个数转换为着色区大小 */  
  40. 40.    offset *= cachep->colour_off;  
  41. 41.  
  42. 42.    if (local_flags & __GFP_WAIT)  
  43. 43.        local_irq_enable();  
  44. 44.  
  45. 45.    /*
  46. 46.     * The test for missing atomic flag is performed here, rather than
  47. 47.     * the more obvious place, simply to reduce the critical path length
  48. 48.     * in kmem_cache_alloc(). If a caller is seriously mis-behaving they
  49. 49.     * will eventually be caught here (where it matters).
  50. 50.     */  
  51. 51.    kmem_flagcheck(cachep, flags);  
  52. 52.  
  53. 53.    /*
  54. 54.     * Get mem for the objs.  Attempt to allocate a physical page from
  55. 55.     * 'nodeid'.
  56. 56.     */  
  57. 57.    if (!objp)/* 还未分配页面,从本内存节点分配1<<cachep->gfporder个页面
  58. 58.    ,objp为slab首页面的虚拟地址 */  
  59. 59.        objp = kmem_getpages(cachep, local_flags, nodeid);  
  60. 60.    if (!objp)  
  61. 61.        goto failed;  
  62. 62.  
  63. 63.    /* Get slab management. */  
  64. 64.     /* 分配slab管理对象 */  
  65. 65.    slabp = alloc_slabmgmt(cachep, objp, offset,  
  66. 66.            local_flags & ~GFP_CONSTRAINT_MASK, nodeid);  
  67. 67.    if (!slabp)  
  68. 68.        goto opps1;  
  69. 69.    /* 设置page到cache、slab的映射 */  
  70. 70.       slab_map_pages(cachep, slabp, objp);  
  71. 71.  
  72. 72.       /* 初始化slab中的对象 */  
  73. 73.    cache_init_objs(cachep, slabp);  
  74. 74.  
  75. 75.    if (local_flags & __GFP_WAIT)  
  76. 76.        local_irq_disable();  
  77. 77.    check_irq_off();  
  78. 78.    spin_lock(&l3->list_lock);  
  79. 79.  
  80. 80.    /* Make slab active. */  
  81. 81.    list_add_tail(&slabp->list, &(l3->slabs_free));  
  82. 82.    /* 更新本cache增长计数 */  
  83. 83.    STATS_INC_GROWN(cachep);  
  84. 84.    /* 更新slab链表中空闲对象计数 */  
  85. 85.    l3->free_objects += cachep->num;  
  86. 86.    spin_unlock(&l3->list_lock);  
  87. 87.    return 1;  
  88. 88.opps1:  
  89. 89.    kmem_freepages(cachep, objp);  
  90. 90.failed:  
  91. 91.    if (local_flags & __GFP_WAIT)  
  92. 92.        local_irq_disable();  
  93. 93.    return 0;  
  94. 94.}  
  95. /*
  96. * Grow (by 1) the number of slabs within a cache.  This is called by
  97. * kmem_cache_alloc() when there are no active objs left in a cache.
  98. */
  99. /*使用一个或多个页面创建一个空slab。
  100. objp:页面虚拟地址,为空表示还未申请内存页,不为空
  101. ,说明已申请内存页,可直接用来创建slab*/
  102. static int cache_grow(struct kmem_cache *cachep,
  103.                 gfp_t flags, int nodeid, void *objp)
  104. {
  105.         struct slab *slabp;
  106.         size_t offset;
  107.         gfp_t local_flags;
  108.         struct kmem_list3 *l3;

  109.         /*
  110.          * Be lazy and only check for valid flags here,  keeping it out of the
  111.          * critical path in kmem_cache_alloc().
  112.          */
  113.         BUG_ON(flags & GFP_SLAB_BUG_MASK);
  114.         local_flags = flags & (GFP_CONSTRAINT_MASK|GFP_RECLAIM_MASK);

  115.         /* Take the l3 list lock to change the colour_next on this node */
  116.         check_irq_off();
  117.          /* 获得本内存节点的slab三链 */
  118.         l3 = cachep->nodelists[nodeid];
  119.         spin_lock(&l3->list_lock);

  120.         /* Get colour for the slab, and cal the next value. */
  121.         /* 获得本slab的着色区偏移 */
  122.         offset = l3->colour_next;
  123.          /* 更新着色区偏移,使不同slab的着色偏移不同 */
  124.         l3->colour_next++;
  125.         /* 不能超过着色区的总大小,如果超过了,重置为0。这就是前面分析过的着色循环问题
  126.         。事实上,如果slab中浪费的空间很少,那么很快就会循环一次。*/
  127.         if (l3->colour_next >= cachep->colour)
  128.                 l3->colour_next = 0;
  129.         spin_unlock(&l3->list_lock);
  130.         /* 将着色单位区间的个数转换为着色区大小 */
  131.         offset *= cachep->colour_off;

  132.         if (local_flags & __GFP_WAIT)
  133.                 local_irq_enable();

  134.         /*
  135.          * The test for missing atomic flag is performed here, rather than
  136.          * the more obvious place, simply to reduce the critical path length
  137.          * in kmem_cache_alloc(). If a caller is seriously mis-behaving they
  138.          * will eventually be caught here (where it matters).
  139.          */
  140.         kmem_flagcheck(cachep, flags);

  141.         /*
  142.          * Get mem for the objs.  Attempt to allocate a physical page from
  143.          * 'nodeid'.
  144.          */
  145.         if (!objp)/* 还未分配页面,从本内存节点分配1<<cachep->gfporder个页面
  146.         ,objp为slab首页面的虚拟地址 */
  147.                 objp = kmem_getpages(cachep, local_flags, nodeid);
  148.         if (!objp)
  149.                 goto failed;

  150.         /* Get slab management. */
  151.          /* 分配slab管理对象 */
  152.         slabp = alloc_slabmgmt(cachep, objp, offset,
  153.                         local_flags & ~GFP_CONSTRAINT_MASK, nodeid);
  154.         if (!slabp)
  155.                 goto opps1;
  156.         /* 设置page到cache、slab的映射 */
  157.        slab_map_pages(cachep, slabp, objp);

  158.        /* 初始化slab中的对象 */
  159.         cache_init_objs(cachep, slabp);

  160.         if (local_flags & __GFP_WAIT)
  161.                 local_irq_disable();
  162.         check_irq_off();
  163.         spin_lock(&l3->list_lock);

  164.         /* Make slab active. */
  165.         list_add_tail(&slabp->list, &(l3->slabs_free));
  166.         /* 更新本cache增长计数 */
  167.         STATS_INC_GROWN(cachep);
  168.         /* 更新slab链表中空闲对象计数 */
  169.         l3->free_objects += cachep->num;
  170.         spin_unlock(&l3->list_lock);
  171.         return 1;
  172. opps1:
  173.         kmem_freepages(cachep, objp);
  174. failed:
  175.         if (local_flags & __GFP_WAIT)
  176.                 local_irq_disable();
  177.         return 0;
  178. }
复制代码
执行流程:

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

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

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

view plaincopy to clipboardprint?
  1. 01./*分配slab管理对象*/  
  2. 02.static struct slab *alloc_slabmgmt(struct kmem_cache *cachep, void *objp,  
  3. 03.                   int colour_off, gfp_t local_flags,  
  4. 04.                   int nodeid)  
  5. 05.{  
  6. 06.    struct slab *slabp;  
  7. 07.  
  8. 08.    if (OFF_SLAB(cachep)) {  
  9. 09.        /* Slab management obj is off-slab. */  
  10. 10.        /* 外置式slab。从general slab cache中分配一个管理对象,
  11. 11.        slabp_cache指向保存有struct slab对象的general slab cache。
  12. 12.        slab初始化阶段general slab cache可能还未创建,slabp_cache指针为空
  13. 13.        ,故初始化阶段创建的slab均为内置式slab。*/  
  14. 14.        slabp = kmem_cache_alloc_node(cachep->slabp_cache,  
  15. 15.                          local_flags, nodeid);  
  16. 16.        /*
  17. 17.         * If the first object in the slab is leaked (it's allocated
  18. 18.         * but no one has a reference to it), we want to make sure
  19. 19.         * kmemleak does not treat the ->s_mem pointer as a reference
  20. 20.         * to the object. Otherwise we will not report the leak.
  21. 21.         *//* 对第一个对象做检查 */  
  22. 22.        kmemleak_scan_area(slabp, offsetof(struct slab, list),  
  23. 23.                   sizeof(struct list_head), local_flags);  
  24. 24.        if (!slabp)  
  25. 25.            return NULL;  
  26. 26.    } else {/* 内置式slab。objp为slab首页面的虚拟地址,加上着色偏移
  27. 27.    ,得到slab管理对象的虚拟地址 */  
  28. 28.        slabp = objp + colour_off;  
  29. 29.        /* 计算slab中第一个对象的页内偏移,slab_size保存slab管理对象的大小
  30. 30.        ,包含struct slab对象和kmem_bufctl_t数组 */  
  31. 31.        colour_off += cachep->slab_size;  
  32. 32.    } /* 在用(已分配)对象数为0 */  
  33. 33.    slabp->inuse = 0;  
  34. 34.    /* 第一个对象的页内偏移,可见对于内置式slab,colouroff成员不仅包括着色区
  35. 35.    ,还包括管理对象占用的空间
  36. 36.    ,外置式slab,colouroff成员只包括着色区。*/  
  37. 37.    slabp->colouroff = colour_off;  
  38. 38.    /* 第一个对象的虚拟地址 */  
  39. 39.    slabp->s_mem = objp + colour_off;  
  40. 40.    /* 内存节点ID */  
  41. 41.    slabp->nodeid = nodeid;  
  42. 42.    /* 第一个空闲对象索引为0,即kmem_bufctl_t数组的第一个元素 */  
  43. 43.    slabp->free = 0;  
  44. 44.    return slabp;  
  45. 45.}  
  46. /*分配slab管理对象*/
  47. static struct slab *alloc_slabmgmt(struct kmem_cache *cachep, void *objp,
  48.                                    int colour_off, gfp_t local_flags,
  49.                                    int nodeid)
  50. {
  51.         struct slab *slabp;

  52.         if (OFF_SLAB(cachep)) {
  53.                 /* Slab management obj is off-slab. */
  54.                 /* 外置式slab。从general slab cache中分配一个管理对象,
  55.                 slabp_cache指向保存有struct slab对象的general slab cache。
  56.                 slab初始化阶段general slab cache可能还未创建,slabp_cache指针为空
  57.                 ,故初始化阶段创建的slab均为内置式slab。*/
  58.                 slabp = kmem_cache_alloc_node(cachep->slabp_cache,
  59.                                               local_flags, nodeid);
  60.                 /*
  61.                  * If the first object in the slab is leaked (it's allocated
  62.                  * but no one has a reference to it), we want to make sure
  63.                  * kmemleak does not treat the ->s_mem pointer as a reference
  64.                  * to the object. Otherwise we will not report the leak.
  65.                  *//* 对第一个对象做检查 */
  66.                 kmemleak_scan_area(slabp, offsetof(struct slab, list),
  67.                                    sizeof(struct list_head), local_flags);
  68.                 if (!slabp)
  69.                         return NULL;
  70.         } else {/* 内置式slab。objp为slab首页面的虚拟地址,加上着色偏移
  71.         ,得到slab管理对象的虚拟地址 */
  72.                 slabp = objp + colour_off;
  73.                 /* 计算slab中第一个对象的页内偏移,slab_size保存slab管理对象的大小
  74.                 ,包含struct slab对象和kmem_bufctl_t数组 */
  75.                 colour_off += cachep->slab_size;
  76.         } /* 在用(已分配)对象数为0 */
  77.         slabp->inuse = 0;
  78.         /* 第一个对象的页内偏移,可见对于内置式slab,colouroff成员不仅包括着色区
  79.         ,还包括管理对象占用的空间
  80.         ,外置式slab,colouroff成员只包括着色区。*/
  81.         slabp->colouroff = colour_off;
  82.         /* 第一个对象的虚拟地址 */
  83.         slabp->s_mem = objp + colour_off;
  84.         /* 内存节点ID */
  85.         slabp->nodeid = nodeid;
  86.         /* 第一个空闲对象索引为0,即kmem_bufctl_t数组的第一个元素 */
  87.         slabp->free = 0;
  88.         return slabp;
  89. }
复制代码
通过初始化,我们画出下面图像。







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

view plaincopy to clipboardprint?
  1. 01./*设置page到cache、slab的指针,这样就能知道页面所在的cache、slab
  2. 02.    addr:slab首页面虚拟地址*/  
  3. 03.static void slab_map_pages(struct kmem_cache *cache, struct slab *slab,  
  4. 04.               void *addr)  
  5. 05.{  
  6. 06.    int nr_pages;  
  7. 07.    struct page *page;  
  8. 08.    /* 获得slab首页面*/  
  9. 09.    page = virt_to_page(addr);  
  10. 10.  
  11. 11.    nr_pages = 1;  
  12. 12.     /* 如果不是大页面(关于大页面请参阅相关文档)
  13. 13.     ,计算页面的个数 */  
  14. 14.    if (likely(!PageCompound(page)))  
  15. 15.        nr_pages <<= cache->gfporder;  
  16. 16.  
  17. 17.    do {  
  18. 18.        /* struct page结构中的lru根据页面的用途有不同的含义
  19. 19.        ,当页面空闲或用于高速缓存时,
  20. 20.        lru成员用于构造双向链表将page串联起来,而当page用于slab时,
  21. 21.        next指向page所在的cache,prev指向page所在的slab */  
  22. 22.        page_set_cache(page, cache);  
  23. 23.        page_set_slab(page, slab);  
  24. 24.        page++;  
  25. 25.    } while (--nr_pages);  
  26. 26.}  
  27. /*设置page到cache、slab的指针,这样就能知道页面所在的cache、slab
  28.         addr:slab首页面虚拟地址*/
  29. static void slab_map_pages(struct kmem_cache *cache, struct slab *slab,
  30.                            void *addr)
  31. {
  32.         int nr_pages;
  33.         struct page *page;
  34.         /* 获得slab首页面*/
  35.         page = virt_to_page(addr);

  36.         nr_pages = 1;
  37.          /* 如果不是大页面(关于大页面请参阅相关文档)
  38.          ,计算页面的个数 */
  39.         if (likely(!PageCompound(page)))
  40.                 nr_pages <<= cache->gfporder;

  41.         do {
  42.                 /* struct page结构中的lru根据页面的用途有不同的含义
  43.                 ,当页面空闲或用于高速缓存时,
  44.                 lru成员用于构造双向链表将page串联起来,而当page用于slab时,
  45.                 next指向page所在的cache,prev指向page所在的slab */
  46.                 page_set_cache(page, cache);
  47.                 page_set_slab(page, slab);
  48.                 page++;
  49.         } while (--nr_pages);
  50. }
复制代码
代码实现结果如下图

5,初始化slab中kmem_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]获得下一个空闲对象的索引并用它更新静态链表。

view plaincopy to clipboardprint?
  1. 01./*初始化slab中的对象,主要是通过kmem_bufctl_t数组将对象串联起来*/  
  2. 02.static void cache_init_objs(struct kmem_cache *cachep,  
  3. 03.                struct slab *slabp)  
  4. 04.{  
  5. 05.    int i;  
  6. 06.    /* 逐一初始化slab中的对象 */  
  7. 07.    for (i = 0; i < cachep->num; i++) {  
  8. 08.         /* 获得slab中第i个对象 */  
  9. 09.        void *objp = index_to_obj(cachep, slabp, i);  
  10. 10.#if DEBUG   
  11. 11.        /* need to poison the objs? */  
  12. 12.        if (cachep->flags & SLAB_POISON)  
  13. 13.            poison_obj(cachep, objp, POISON_FREE);  
  14. 14.        if (cachep->flags & SLAB_STORE_USER)  
  15. 15.            *dbg_userword(cachep, objp) = NULL;  
  16. 16.  
  17. 17.        if (cachep->flags & SLAB_RED_ZONE) {  
  18. 18.            *dbg_redzone1(cachep, objp) = RED_INACTIVE;  
  19. 19.            *dbg_redzone2(cachep, objp) = RED_INACTIVE;  
  20. 20.        }  
  21. 21.        /*
  22. 22.         * Constructors are not allowed to allocate memory from the same
  23. 23.         * cache which they are a constructor for.  Otherwise, deadlock.
  24. 24.         * They must also be threaded.
  25. 25.         */  
  26. 26.        if (cachep->ctor && !(cachep->flags & SLAB_POISON))  
  27. 27.            cachep->ctor(objp + obj_offset(cachep));  
  28. 28.  
  29. 29.        if (cachep->flags & SLAB_RED_ZONE) {  
  30. 30.            if (*dbg_redzone2(cachep, objp) != RED_INACTIVE)  
  31. 31.                slab_error(cachep, "constructor overwrote the"  
  32. 32.                       " end of an object");  
  33. 33.            if (*dbg_redzone1(cachep, objp) != RED_INACTIVE)  
  34. 34.                slab_error(cachep, "constructor overwrote the"  
  35. 35.                       " start of an object");  
  36. 36.        }  
  37. 37.        if ((cachep->buffer_size % PAGE_SIZE) == 0 &&  
  38. 38.                OFF_SLAB(cachep) && cachep->flags & SLAB_POISON)  
  39. 39.            kernel_map_pages(virt_to_page(objp),  
  40. 40.                     cachep->buffer_size / PAGE_SIZE, 0);  
  41. 41.#else   
  42. 42.        /* 调用此对象的构造函数 */  
  43. 43.        if (cachep->ctor)  
  44. 44.            cachep->ctor(objp);  
  45. 45.#endif /* 初始时所有对象都是空闲的,只需按照数组顺序串起来即可 */   
  46. 46.        /*相当于静态索引指针*/  
  47. 47.        slab_bufctl(slabp)[i] = i + 1;  
  48. 48.    }  
  49. 49.    /* 最后一个指向BUFCTL_END */  
  50. 50.    slab_bufctl(slabp)[i - 1] = BUFCTL_END;  
  51. 51.}  
复制代码

论坛徽章:
0
2 [报告]
发表于 2012-01-10 15:15 |只看该作者
谢谢分享
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP