kmalloc()->__kmalloc()->__do_kmalloc()
/** * __do_kmalloc - allocate memory * @size: how many bytes of memory are required. * @flags: the type of memory to allocate (see kmalloc). * @caller: function caller for debug tracking of the caller */ static __always_inline void *__do_kmalloc(size_t size, gfp_t flags, void *caller) { struct kmem_cache *cachep; void *ret; /* If you want to save a few bytes .text space: replace * __ with kmem_. * Then kmalloc uses the uninlined functions instead of the inline * functions. */ /*查找指定大小的通用cache,关于sizes[]数组,在前面 的初始化中就已经分析过了*/ cachep = __find_general_cachep(size, flags); if (unlikely(ZERO_OR_NULL_PTR(cachep))) return cachep; /*实际的分配工作*/ ret = __cache_alloc(cachep, flags, caller); trace_kmalloc((unsigned long) caller, ret, size, cachep->buffer_size, flags); return ret; }
实际的分配工作:__do_cache_alloc()->__cache_alloc()->____cache_alloc()
/*从指定cache中分配对象*/ static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags) { void *objp; struct array_cache *ac; check_irq_off(); /* 获得本CPU的local cache */ ac = cpu_cache_get(cachep); /* 如果local cache中有可用的空闲对象 */ if (likely(ac->avail)) { /* 更新local cache命中计数 */ STATS_INC_ALLOCHIT(cachep); /* touched置1表示最近使用了local cache,这会影响填充 local cache时的数目,最近使用的填充较多的对象 */ ac->touched = 1; /* 从local cache的entry数组中提取最后面的空闲对象 */ objp = ac->entry[--ac->avail]; } else { /* local cache中没有空闲对象,更新未命中计数 */ STATS_INC_ALLOCMISS(cachep); /* 从slab三链中提取空闲对象填充到local cache中 */ objp = cache_alloc_refill(cachep, flags); #if 0/*这里是我新加的,这里可能是这个版本的一个bug,在最新的内核里面这块已经加上了*/ /* * the 'ac' may be updated by cache_alloc_refill(), * and kmemleak_erase() requires its correct value. */ /* cache_alloc_refill的cache_grow打开了中断,local cache指针可能发生了变化,需要重新获得 */ ac = cpu_cache_get(cachep); #endif } /* * To avoid a false negative, if an object that is in one of the * per-CPU caches is leaked, we need to make sure kmemleak doesn't * treat the array pointers as a reference to the object. */ /* 分配出去的对象,其entry指针指向空 */ kmemleak_erase(&ac->entry[ac->avail]); return objp; }
该函数的执行流程:
1,从本地CPU cache中查找是否有空闲的对象;
2,如果本地CPU cache 中没有空闲对象,从slab三链中提取空闲对象,此操作由函数cache_alloc_refill()实现
1)如果存在共享本地cache,那么将共享本地cache中的对象批量复制到本地cache。
2)如果没有shared local cache,或是其中没有空闲的对象,从slab链表中分配,其中,从slab中分配时,先查看部分空余链表,然后再查看空余链表。将slab链表中的数据先放到本地CPU cache中。
3) 如果本地CPU cache中任然没有数据,那么只有重新创建一个slab,然后再试。
/*从slab三链中提取一部分空闲对象填充到local cache中*/ static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags) { int batchcount; struct kmem_list3 *l3; struct array_cache *ac; int node; retry: check_irq_off(); /* 获得本内存节点,UMA只有一个节点 */ node = numa_node_id(); /* 获得本CPU的local cache */ ac = cpu_cache_get(cachep); /* 批量填充的数目,local cache是按批填充的 */ batchcount = ac->batchcount; if (!ac->touched && batchcount > BATCHREFILL_LIMIT) { /* * If there was little recent activity on this cache, then * perform only a partial refill. Otherwise we could generate * refill bouncing. */ /* 最近未使用过此local cache,没有必要添加过多的对象 ,添加的数目为默认的限定值 */ batchcount = BATCHREFILL_LIMIT; } /* 获得本内存节点、本cache的slab三链 */ l3 = cachep->nodelists[node]; BUG_ON(ac->avail > 0 || !l3); spin_lock(&l3->list_lock); /* See if we can refill from the shared array */ /* shared local cache用于多核系统中,为所有cpu共享 ,如果slab cache包含一个这样的结构 ,那么首先从shared local cache中批量搬运空闲对象到local cache中 。通过shared local cache使填充工作变得简单。*/ if (l3->shared && transfer_objects(ac, l3->shared, batchcount)) goto alloc_done; /* 如果没有shared local cache,或是其中没有空闲的对象 ,从slab链表中分配 */ while (batchcount > 0) { struct list_head *entry; struct slab *slabp; /* Get slab alloc is to come from. */ /* 先从部分满slab链表中分配 */ entry = l3->slabs_partial.next; /* next指向头节点本身,说明部分满slab链表为空 */ if (entry == &l3->slabs_partial) { /* 表示刚刚访问了slab空链表 */ l3->free_touched = 1; /* 检查空slab链表 */ entry = l3->slabs_free.next; /* 空slab链表也为空,必须增加slab了 */ if (entry == &l3->slabs_free) goto must_grow; } /* 获得链表节点所在的slab */ slabp = list_entry(entry, struct slab, list); /*调试用*/ check_slabp(cachep, slabp); check_spinlock_acquired(cachep); /* * The slab was either on partial or free list so * there must be at least one object available for * allocation. */ BUG_ON(slabp->inuse >= cachep->num); while (slabp->inuse < cachep->num && batchcount--) { /* 更新调试用的计数器 */ STATS_INC_ALLOCED(cachep); STATS_INC_ACTIVE(cachep); STATS_SET_HIGH(cachep); /* 从slab中提取一个空闲对象,将其虚拟地址插入到local cache中 */ ac->entry[ac->avail++] = slab_get_obj(cachep, slabp, node); } check_slabp(cachep, slabp); /* move slabp to correct slabp list: */ /* 从原链表中删除此slab节点,list表示此 slab位于哪个链表(满、部分满、空)中 */ list_del(&slabp->list); /*因为从中删除了一个slab,需要从新检查*/ if (slabp->free == BUFCTL_END) /* 此slab中已经没有空闲对象,添加到“full”slab链表中 */ list_add(&slabp->list, &l3->slabs_full); else /* 还有空闲对象,添加到“partial”slab链表中 */ list_add(&slabp->list, &l3->slabs_partial); } must_grow: /* 前面从slab链表中添加avail个空闲对象到local cache中 ,更新slab链表的空闲对象数 */ l3->free_objects -= ac->avail; alloc_done: spin_unlock(&l3->list_lock); /* local cache中仍没有可用的空闲对象,说明slab 三链中也没有空闲对象,需要创建新的空slab了 */ if (unlikely(!ac->avail)) { int x; /* 创建一个空slab */ x = cache_grow(cachep, flags | GFP_THISNODE, node, NULL); /* cache_grow can reenable interrupts, then ac could change. */ /* 上面的操作使能了中断,此期间local cache指针可能发生了变化,需要重新获得 */ ac = cpu_cache_get(cachep); /* 无法新增空slab,local cache中也没有空闲对象,表明系统已经无法分配新的空闲对象了 */ if (!x && ac->avail == 0) /* no objects in sight? abort */ return NULL; /* 走到这有两种可能,第一种是无论新增空slab成功或失败,只要avail不为0 ,表明是其他进程重填了local cache,本进程就不需要重填了 ,不执行retry流程。第二种是avail为0,并且新增空slab成功 ,则进入retry流程,利用新分配的空slab填充local cache */ if (!ac->avail) /* objects refilled by interrupt? */ goto retry; } /* 重填了local cache,设置近期访问标志 */ ac->touched = 1; /* 返回local cache中最后一个空闲对象的虚拟地址 */ return ac->entry[--ac->avail]; }
几个涉及到的辅助函数
/* * Transfer objects in one arraycache to another. * Locking must be handled by the caller. * * Return the number of entries transferred. */ static int transfer_objects(struct array_cache *to, struct array_cache *from, unsigned int max) { /* Figure out how many entries to transfer */ int nr = min(min(from->avail, max), to->limit - to->avail); if (!nr) return 0; /*拷贝*/ memcpy(to->entry + to->avail, from->entry + from->avail -nr, sizeof(void *) *nr); /*两边数据更新*/ from->avail -= nr; to->avail += nr; to->touched = 1; return nr; }
/*从slab中提取一个空闲对象*/ static void *slab_get_obj(struct kmem_cache *cachep, struct slab *slabp, int nodeid) { /* 获得一个空闲的对象,free是本slab中第一个空闲对象的索引 */ void *objp = index_to_obj(cachep, slabp, slabp->free); kmem_bufctl_t next; /* 更新在用对象计数 */ slabp->inuse++; /* 获得下一个空闲对象的索引 */ next = slab_bufctl(slabp)[slabp->free]; #if DEBUG slab_bufctl(slabp)[slabp->free] = BUFCTL_FREE; WARN_ON(slabp->nodeid != nodeid); #endif /* free指向下一个空闲的对象 */ slabp->free = next; return objp; }
static inline void *index_to_obj(struct kmem_cache *cache, struct slab *slab, unsigned int idx) { /* s_mem是slab中第一个对象的起始地址,buffer_size是每个对象的大小 ,这里根据对象索引计算对象的地址 */ return slab->s_mem + cache->buffer_size * idx; }
static inline kmem_bufctl_t *slab_bufctl(struct slab *slabp) { return (kmem_bufctl_t *) (slabp + 1); }
static inline struct array_cache *cpu_cache_get(struct kmem_cache *cachep) { return cachep->array[smp_processor_id()]; }总结:从slab分配器中分配空间实际工作很简单,先查看本地CPU cache,然后是本地共享CPU cache,最后是三链。前面三个都没有空间时,需要从新分配slab。可以看出,从slab分配器中分配内存空间一般不会申请不到空间,也就是说返回空的可能性很小。