Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2111505
  • 博文数量: 438
  • 博客积分: 3871
  • 博客等级: 中校
  • 技术积分: 6075
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-10 00:11
个人简介

邮箱: wangcong02345@163.com

文章分类

全部博文(438)

文章存档

2017年(15)

2016年(119)

2015年(91)

2014年(62)

2013年(56)

2012年(79)

2011年(16)

分类: LINUX

2016-11-28 13:58:11

一.总体分析


二.代码分析
2.1 在mm/slab.c中L1535 kmalloc
  1. void * kmalloc (size_t size, int flags)
  2. {
  3.     cache_sizes_t *csizep = cache_sizes;  
  4. //扫描cache_sizes数组,找到比欲分配的size还大的csizep
  5.     for (; csizep->cs_size; csizep++) {
  6.         if (size > csizep->cs_size)
  7.             continue;
  8.    //cache_sizes包含cs_cachep与cs_dmacachep,通过flags=GFP_DMA来区分
  9.         return __kmem_cache_alloc(flags & GFP_DMA csizep->cs_dmacachep : csizep->cs_cachep, flags); //2.2
  10.     }
  11.     return NULL;
  12. }
start_kernel-->vfs_caches_init-->mnt_init-->init_mount_tree-->do_kern_mount-->set_devname
size=7(‘rootfs\0' strlen(name)+1), flags=GFP_KERNEL

2.2 在mm/slab.c中L1318 kmalloc-->__kmem_cache_alloc
  1. static inline void * __kmem_cache_alloc (kmem_cache_t *cachep, int flags)
  2. {
  3.     unsigned long save_flags;
  4.     void* objp;
  5. //只是检查标志位是不是合法,没有什么作用
  6.     kmem_cache_alloc_head(cachep, flags)
  7. try_again:
  8.     local_irq_save(save_flags);
  9. #ifdef CONFIG_SMP
  10.     {
  11.         cpucache_t *cc = cc_data(cachep);    //执行后cc=0x0

  12.         if (cc) {
  13.             if (cc->avail) {
  14.                 STATS_INC_ALLOCHIT(cachep);
  15.                 objp = cc_entry(cc)[--cc->avail];
  16.             } else {
  17.                 STATS_INC_ALLOCMISS(cachep);
  18.                 objp = kmem_cache_alloc_batch(cachep,cc,flags);
  19.                 if (!objp)
  20.                     goto alloc_new_slab_nolock;
  21.             }
  22.         } else {
  23.             spin_lock(&cachep->spinlock);         //smp需要加锁了
  24.             objp = kmem_cache_alloc_one(cachep);  //2.3如果从slab->free中分配不到slab就跳到alloc_new_slab
  25.             spin_unlock(&cachep->spinlock);       //alloc_new_slab完之后事slab->free
  26.         }
  27.     }
  28. #else
  29.     objp = kmem_cache_alloc_one(cachep);
  30. #endif
  31.     local_irq_restore(save_flags);
  32.     return objp;
  33. alloc_new_slab:                      //kmem_cache_alloc_one是一个宏定义,如果slabs->free中也没有就跳到这儿
  34. #ifdef CONFIG_SMP
  35.     spin_unlock(&cachep->spinlock);
  36. alloc_new_slab_nolock:
  37. #endif
  38.     local_irq_restore(save_flags);
  39.     if (kmem_cache_grow(cachep, flags))   //2.4
  40.         goto try_again;                   //申请slab之后再跳回去,到上面的try_agin
  41.     return NULL;
  42. }
2.3 这个函数要调用两次,第1次时因为slabs_free是空的,所以要goto alloc_new_slab
第2次时slabs_free不为空,可以分配到kmem_cache
  1. #define kmem_cache_alloc_one(cachep)                
  2. ({                                                  
  3.     struct list_head * slabs_partial, * entry;      
  4.     slab_t *slabp;                                                                  
  5.     slabs_partial = &(cachep)->slabs_partial;  //取slabs_partial的头结点
  6.     entry = slabs_partial->next;               //因为slabs_partial此时还为空,所以这个entry=NULL
  7.     if (unlikely(entry == slabs_partial)) {    //判断slabs_partial的链表是否为空,这儿是空的
  8.         struct list_head * slabs_free;            
  9.         slabs_free = &(cachep)->slabs_free;    //取slabs_free的头结点       
  10.         entry = slabs_free->next;              //因为slabs_free此时还为空,所以这个entry=NULL        
  11.         if (unlikely(entry == slabs_free))     //判断slabs_free的链表是否为空,这儿是空的     
  12.             goto alloc_new_slab;               //所以要跳到alloc_new_slab中
  13.         list_del(entry);                       
  14.         list_add(entry, slabs_partial);             
  15.     }
  16.                                                     
  17.     slabp = list_entry(entry, slab_t, list);        
  18.     kmem_cache_alloc_one_tail(cachep, slabp);     //2.3.1
  19. })

在函数kmalloc中查找到的cachep是size-32的这个cachep
  1. (gdb) p *cachep 地址=0xc210b080
  2. $6 = {slabs_full = {next = 0xc210b080, prev = 0xc210b080}, slabs_partial = {next = 0xc210b088, prev = 0xc210b088}, slabs_free = {next = 0xc210b090, prev = 0xc210b090},
  3.   objsize =0x40=64, flags = 0x22000, num = 58, spinlock = {lock = 0, magic = 0xdead4ead}, batchcount = 0, gfporder = 0, gfpflags = 0, colour = 1, colour_off = 128, colour_next = 0,
  4.   slabp_cache = 0x0, growing = 0, dflags = 0, ctor = 0x0, dtor = 0x0, failures = 0, name = "size-32", '\000' <repeats 12 times>, next = {next = 0xc02cf830 <cache_cache+112>,
  5.     prev = 0xc210b1e8}, cpudata = {0x0 <repeats 32 times>}}

2.4 分配内存并初始化slabp
  1. static int kmem_cache_grow (kmem_cache_t * cachep, int flags)
  2. {
  3.     slab_t    *slabp;
  4.     struct page    *page;
  5.     void        *objp;
  6.     size_t         offset;
  7.     unsigned int     i, local_flags;
  8.     unsigned long     ctor_flags;
  9.     unsigned long     save_flags;

  10.     ...  //省略一些判断

  11.     ctor_flags = SLAB_CTOR_CONSTRUCTOR;        //执行后ctor_flags=0x1
  12.     local_flags = (flags & SLAB_LEVEL_MASK);   //执行后local_flags=0x1f0
  13.     if (local_flags == SLAB_ATOMIC)
  14.         ctor_flags |= SLAB_CTOR_ATOMIC;

  15.     /* About to mess with non-constant members - lock. */
  16.     spin_lock_irqsave(&cachep->spinlock, save_flags);

  17.     /* Get colour for the slab, and cal the next value. */
  18.     offset = cachep->colour_next;              //此时cachep->colour_next=0x0执行后offset=0x0
  19.     cachep->colour_next++;                     //执行后cachep->colour_next=0x1
  20.     if (cachep->colour_next >= cachep->colour//此时colour_next=0x1, colour=0x1
  21.         cachep->colour_next = 0;               //执行后cachep->colour_next=0x0   
  22.     offset *= cachep->colour_off;              //执行前offset=0x0,所以执行后offset=0x0
  23.     cachep->dflags |= DFLGS_GROWN;             //执行后cachep->dflags=0x01

  24.     cachep->growing++;                         //执行前growing=0x0,所以执行后growing=0x1
  25.     spin_unlock_irqrestore(&cachep->spinlock, save_flags);

  26.     /* Get mem for the objs. */
  27.     if (!(objp = kmem_getpages(cachep, flags)))   //从zone_normal处分配一页内存0xc210c000
  28.         goto failed;
  29.     //计算slab管理区的大小 --> 参数offset=0x0,local_flags=0x1f0
  30.     //执行后--> p *slabp = 0xc210c000 = {list = {next = 0x0, prev = 0x0}, colouroff =256=0x100, s_mem = 0xc210c100, inuse = 0, free = 0}
  31.     if (!(slabp = kmem_cache_slabmgmt(cachep, objp, offset, local_flags))) //2.4.1执行后slabp指向0xc210c000,计算slab的管理区大小
  32.         goto opps1;

  33.     /* I hope this is OK. */
  34.     i = 1 << cachep->gfporder;                    //执行前cachep->gfporder=0x0,所以执行后i=0x1
  35.     page = virt_to_page(objp);                    //将虚地址objp转为mem_map中的管理page
  36.     do {
  37.         SET_PAGE_CACHE(page, cachep);             //page->list.next=cachep
  38.         SET_PAGE_SLAB(page, slabp);               //page->list.pre=slabp
  39.         PageSetSlab(page);                        //将page->flags设为PG_slab
  40.         page++;
  41.     } while (--i);

  42.     kmem_cache_init_objs(cachep, slabp, ctor_flags);    //2.4.2 初始化slab的管理区
  43.     spin_lock_irqsave(&cachep->spinlock, save_flags);
  44.     cachep->growing--;                            //执行后cachep->growing=0x0

  45.     /* Make slab active. */
  46.     list_add_tail(&slabp->list, &cachep->slabs_free);  //把申请到的slab加到slabs_frees中
  47.     STATS_INC_GROWN(cachep);                     //使cachep->grown++,执行后cachep->grown=0x01
  48.     cachep->failures = 0;

  49.     spin_unlock_irqrestore(&cachep->spinlock, save_flags);
  50.     return 1;
  51. opps1:
  52.     kmem_freepages(cachep, objp);
  53. failed:
  54.     spin_lock_irqsave(&cachep->spinlock, save_flags);
  55.     cachep->growing--;
  56.     spin_unlock_irqrestore(&cachep->spinlock, save_flags);
  57.     return 0;
  58. }

2.4.1 按照cachp->num计算colour_off的值,即slab管理区的大小
参数说明: objp是刚从zone_normal中分配的一页内存的首地址0xc210c000
colour_off=0x0, local_flags=0x1F0=GFP_KERNEL
  1. static inline slab_t * kmem_cache_slabmgmt (kmem_cache_t *cachep,
  2.             void *objp, int colour_off, int local_flags)
  3. {
  4.     slab_t *slabp;
  5.     
  6.     if (OFF_SLAB(cachep)) {
  7.         /* Slab management obj is off-slab. */
  8.         slabp = kmem_cache_alloc(cachep->slabp_cache, local_flags);
  9.         if (!slabp)
  10.             return NULL;
  11.     } else {
  12. //计算colour_off,如果cachep->num很多的话,这个colour_off也会变大,而不是固定1个L1_CACHE_ALIGN
  13.         slabp = objp+colour_off;    //执行前colour_off=0x0,执行后slabp是申请到的一页内存的首地址0xc210c000
  14.         colour_off += L1_CACHE_ALIGN(cachep->num * sizeof(kmem_bufctl_t) + sizeof(slab_t))//执行后colour_off=256个
  15.     }
  16.     slabp->inuse = 0;
  17.     slabp->colouroff = colour_off;
  18.     slabp->s_mem = objp+colour_off;
  19.     //执行后{list = {next = 0x0, prev = 0x0}, colouroff = 256, s_mem = 0xc210c100, inuse = 0, free = 0}
  20.     return slabp;
  21. }
2.4.2  初始化slab的管理区
  1. static inline void kmem_cache_init_objs (kmem_cache_t * cachep,
  2.             slab_t * slabp, unsigned long ctor_flags)
  3. {
  4.     int i;

  5.     for (= 0; i < cachep->num; i++) {                //cachep->num=58
  6.         void* objp = slabp->s_mem+cachep->objsize*i;   //slabp->s_mem=0xc210c100
  7.         if (cachep->ctor)
  8.             cachep->ctor(objp, cachep, ctor_flags);    //这儿不执行
  9.         slab_bufctl(slabp)[i] = i+1;
  10.     }
  11.     slab_bufctl(slabp)[i-1] = BUFCTL_END;             //#define slab_bufctl(slabp) (kmem_bufctl_t *)(((slab_t*)slabp)+1))
  12.     slabp->free = 0;
  13. }
执行后:
  1. (gdb) p *slabp   = 0xc210c000
  2. $15 = {list = {next = 0x0, prev = 0x0}, colouroff = 256, s_mem = 0xc210c100, inuse = 0, free = 0}
  3. (gdb) x /128wx slabp
  4. 0xc210c000:    0x00000000    0x00000000    0x00000100    0xc210c100
  5. 0xc210c010:    0x00000000    0x00000000    0x00000001    0x00000002
  6. 0xc210c020:    0x00000003    0x00000004    0x00000005    0x00000006
  7. 0xc210c030:    0x00000007    0x00000008    0x00000009    0x0000000a
  8. 0xc210c040:    0x0000000b    0x0000000c    0x0000000d    0x0000000e
  9. 0xc210c050:    0x0000000f    0x00000010    0x00000011    0x00000012
  10. 0xc210c060:    0x00000013    0x00000014    0x00000015    0x00000016
  11. 0xc210c070:    0x00000017    0x00000018    0x00000019    0x0000001a
  12. 0xc210c080:    0x0000001b    0x0000001c    0x0000001d    0x0000001e
  13. 0xc210c090:    0x0000001f    0x00000020    0x00000021    0x00000022
  14. 0xc210c0a0:    0x00000023    0x00000024    0x00000025    0x00000026
  15. 0xc210c0b0:    0x00000027    0x00000028    0x00000029    0x0000002a
  16. 0xc210c0c0:    0x0000002b    0x0000002c    0x0000002d    0x0000002e
  17. 0xc210c0d0:    0x0000002f    0x00000030    0x00000031    0x00000032
  18. 0xc210c0e0:    0x00000033    0x00000034    0x00000035    0x00000036
  19. 0xc210c0f0:    0x00000037    0x00000038    0x00000039    0xffffffff
  20. 0xc210c100:    0x00000000    0x00000000    0x00000000    0x00000000
对比kmem_cache_create时的slapb

  1. (gdb) p *slabp
    $19 = {list = {next = 0x0, prev = 0x0}, colouroff = 128, s_mem = 0xc210b080, inuse = 0, free = 0}

  2. (gdb) x /64wx slabp
  3. 0xc210b000:    0x00000000    0x00000000    0x00000080    0xc210b080
  4. 0xc210b010:    0x00000000    0x00000000    0x00000001    0x00000002
  5. 0xc210b020:    0x00000003    0x00000004    0x00000005    0x00000006
  6. 0xc210b030:    0x00000007    0x00000008    0x00000009    0x0000000a
  7. 0xc210b040:    0x0000000b    0x0000000c    0x0000000d    0x0000000e
  8. 0xc210b050:    0x0000000f    0xffffffff    0x00000000    0x00000000      //由原先的0x00000010-->0xFFFFFFFF代表是END
注意:slab_bufctl(slabp)中存的是下一项的值:
例如第0项存的是1,代表下一个是第1项;  第16项存的是0xFFFFFFFF代表结束
2.3.1 
下面是第2次进入kmem_cache_alloc_one时会调用kmem_cache_alloc_one_tail
  1. static inline void * kmem_cache_alloc_one_tail (kmem_cache_t *cachep, slab_t *slabp)
  2. {
  3.     void *objp;
  4.     //slabp=0xc210c000
  5.     //里面的内容是{list = {next = 0xc210b088, prev = 0xc210b088}, colouroff = 256, s_mem = 0xc210c100, inuse = 1, free = 0}
  6.     STATS_INC_ALLOCED(cachep);
  7.     STATS_INC_ACTIVE(cachep);
  8.     STATS_SET_HIGH(cachep);

  9.     slabp->inuse++;                                    //执行后slabp->inuse=1
  10.     objp = slabp->s_mem + slabp->free*cachep->objsize//目前slabp->free=0,所以执行后objp=0xc210c100
  11.     slabp->free=slab_bufctl(slabp)[slabp->free];       //执行后slabp->free=1,slab_bufctl(slabp)[slabp->free]是下一项的值

  12.     if (unlikely(slabp->free == BUFCTL_END)) {         //如果slabp->free到头了,说明没有剩余的了,如果当前是第57项,即slab可以分配的最后一项
  13.         list_del(&slabp->list);                        //就把slabp->list清空,
  14.         list_add(&slabp->list, &cachep->slabs_full);   //放到slabs_full中。然后下一次kmem_cache_alloc_one中知道slabs->free与slabs->part都为空。
  15.     }
  16.     return objp;                                       //返回0xc210c100
  17. }
解释kmem_cache_t的获取,即源码中的objp句,如下图所示:

2.5 总结
将上图中写0的黄色小框的地址0xc210c100返回,那么这个0xc210c100就是kmalloc申请到的地址。
下一次再在size-32中申请时,会返回写1的黄色小框的地址,
....
直到返回写57的黄色小框的地址之后,己经全部都占用了,就把这个slab->partial这个list中指向这一页的结点删掉,插入到slabs->full队列中。
最终形成了如下的链表


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