Slub分配器的来龙去脉( 四 )

下面是每个函数的主干分析,代码有精简 。
kmem_cache_create():
kmem_cache_create()里继续调用了
kmem_cache_create_usercopy() 。
kmem_cache_create() { return kmem_cache_create_usercopy(name, size, align, flags, 0, 0, ctor);}kmem_cache_create_usercopy():
kmem_cache_create_usercopy() { struct kmem_cache *s = NULL; const char *cache_name;/** Some allocators will constraint the set of valid flags to a subset* of all flags. We expect them to define CACHE_CREATE_MASK in this* case, and we'll just provide them with a sanitized version of the* passed flags.*/ flags &= CACHE_CREATE_MASK;/* 定义这个缓存的名字,用于在/proc/slabinfo中显示 */ cache_name = kstrdup_const(name, GFP_KERNEL);/* kmem_cache结构,并返回其地址 */ s = create_cache(cache_name, size,calculate_alignment(flags, align, size),flags, useroffset, usersize, ctor, NULL, NULL);return s;}create_cache():
create_cache() { struct kmem_cache *s; int err; /* 为kmem_cache结构申请一段内存并清零 */ s = kmem_cache_zalloc(kmem_cache, GFP_KERNEL); /* 初始化kmem_cache结构的部分成员 */ s->name = name; s->size = s->object_size = object_size; s->align = align; s->ctor = ctor; s->useroffset = useroffset; s->usersize = usersize; /* 核心函数,slub/slab/slob都实现了这个函数 */ err = __kmem_cache_create(s, flags);/* 将新创建的kmem_cache加入slab caches链表 */ list_add(&s->list, &slab_caches);return s;}__kmem_cache_create():
__kmem_cache_create() {int err;/* 在kmem_cache_open中处理剩余的结构成员,如min_partial、cpu_partial等 */err = kmem_cache_open(s, flags); }kmem_cache_open():
kmem_cache_open() { /* 设置kmem_cache中的min_partial,它表示kmem_cache_node中partial链表可挂入的slab数量 */ set_min_partial(s, ilog2(s->size) / 2);/* 设置kmem_cache中的cpu_partial,它表示per cpu partial上所有slab中free object总数 */ set_cpu_partial(s);/* 为每个节点分配kmem_cache_node */ if (!init_kmem_cache_nodes(s))goto error; /* 为kmem_cache_cpu变量创建每CPU副本 */ if (alloc_kmem_cache_cpus(s))return 0;}分配对象(object)的函数分析函数调用流程:
kmem_cache_alloc()——> slab_alloc()——> slab_alloc_node()——> __slab_alloc()——> ___slab_alloc()kmem_cache_alloc():
kmem_cache_alloc() { /* 直接调用slab_alloc */ void *ret = slab_alloc(s, gfpflags, _RET_IP_); return ret;}slab_alloc():
slab_alloc() { return slab_alloc_node(s, gfpflags, NUMA_NO_NODE, addr);}slab_alloc_node():
slab_alloc_node() { void *object;struct kmem_cache_cpu *c; struct page *page;redo: /** 要保证tid和kmem_cache是由同一个CPU访问 。但是如果配置了CONFIG_PREEMPT = y,* 即开启了内核抢占后,访问tid和kmem_cache的CPU可能不是同一个CPU,所以要检查* 是否匹配,直到它们是由同一个CPU进行访问 。** 内核态抢占的时机是:* 1.中断处理函数返回内核空间之前会检查请求重新调度的标志(TIF_NEED_RESCHED),* 如果置位则调用preempt_schedule_irq()执行抢占 。* 2. 当内核从non-preemptible(禁止抢占)状态变成preemptible(允许抢占)的时候 。*/ do {tid = this_cpu_read(s->cpu_slab->tid); /* 访问当前CPU的per CPU变量的副本的tid */c = raw_cpu_ptr(s->cpu_slab); } while (IS_ENABLED(CONFIG_PREEMPT) &&/* 检查是否开启了内核抢占 */unlikely(tid != READ_ONCE(c->tid))); barrier(); /* 内存屏障,消除指令乱序执行的影响 */ object = c->freelist;/* 下一个free object的地址 */ page = c->page;/* 当前使用的slab */ if (unlikely(!object || !node_match(page, node))) {/* 调用核心函数__slab_alloc() */object = __slab_alloc(s, gfpflags, node, addr, c);stat(s, ALLOC_SLOWPATH); } else {void *next_object = get_freepointer_safe(s, object);if (unlikely(!this_cpu_cmpxchg_double(s->cpu_slab->freelist, s->cpu_slab->tid,object, tid,next_object, next_tid(tid)))) {note_cmpxchg_failure("slab_alloc", s, tid);goto redo;}prefetch_freepointer(s, next_object);stat(s, ALLOC_FASTPATH); } maybe_wipe_obj_freeptr(s, object); /* 如果gfpflags标志需要对object对象的内存清零 */ if (unlikely(slab_want_init_on_alloc(gfpflags, s)) && object)memset(object, 0, s->object_size); slab_post_alloc_hook(s, gfpflags, 1, &object); return object;}__slab_alloc():
__slab_alloc() { void *p; unsigned long flags;/** 关中断 。关闭当前处理器上的所有中断处理** local_irq_save()将当前的中断状态(开或关)* 保存在flags中然后再禁用处理器上的中断 。** 与local_irq_save不同,local_irq_disable()* 不保存状态而关闭本地处理器的中断服务 。*/ local_irq_save(flags);#ifdef CONFIG_PREEMPT /** 在关中断之前,可能已经被抢占并被调度在不同的CPU上,* 所以需要重新加载CPU区域的指针 。*/ c = this_cpu_ptr(s->cpu_slab);#endif /* 调用核心函数___slab_alloc() */ p = ___slab_alloc(s, gfpflags, node, addr, c);/** 恢复本地处理器的中断 。** local_irq_restore()将local_irq_save()保存的状态值(flags)恢复,* 注意是恢复之前的中断状态,不一定会开启中断 。如果之前的状态是* 开中断,就打开中断;如果之前的状态是关中断,就关闭中断 。* 而local_irq_enable()会无条件开启中断,所以可能会破坏之前的中* 断环境 。所以local_irq_restore()比local_irq_enable()更安全 。*/ local_irq_restore(flags);return p;}


推荐阅读