分类: LINUX
2010-04-24 15:38:37
start_kernel
void __init kmem_cache_init(void)
{
int i;
int caches = 0;
init_alloc_cpu();
/* Able to allocate
the per node structures */
slab_state = PARTIAL;
static enum {
DOWN, /* No slab functionality available */
PARTIAL, /* kmem_cache_open() works but kmalloc
does not */
UP, /* Everything works but does not show
up in sysfs */
SYSFS /* Sysfs up */
} slab_state = DOWN;
设置完状态后然后创建kmem_cache;
/* Caches that are not of the two-to-the-power-of size */
if (KMALLOC_MIN_SIZE
<= 64) {
create_kmalloc_cache(&kmalloc_caches[1],
"kmalloc-96",
96, GFP_KERNEL);
caches++;
create_kmalloc_cache(&kmalloc_caches[2],
"kmalloc-192",
192, GFP_KERNEL);
caches++;
}
for (i =
KMALLOC_SHIFT_LOW; i < SLUB_PAGE_SHIFT; i++) {
create_kmalloc_cache(&kmalloc_caches[i],
"kmalloc",
1 << i, GFP_KERNEL);
caches++;
}
slub.c中。
#ifndef ARCH_KMALLOC_MINALIGN
#define ARCH_KMALLOC_MINALIGN __alignof__(unsigned long long)
#endif
然后在slub_def.h中
/*
* Kmalloc subsystem.
*/
#if defined(ARCH_KMALLOC_MINALIGN) &&
ARCH_KMALLOC_MINALIGN > 8
#define KMALLOC_MIN_SIZE ARCH_KMALLOC_MINALIGN
#else
#define KMALLOC_MIN_SIZE 8
#endif
先判断是否小于64,在如上可知,KMALLOC_MIN_SIZE定义为8,所以创建了一个大小为96的,和一个大小为192的kmem_cahce。
extern struct kmem_cache kmalloc_caches[SLUB_PAGE_SHIFT];
然后进入for语句。
#define KMALLOC_SHIFT_LOW ilog2(KMALLOC_MIN_SIZE)
#define SLUB_MAX_SIZE (2 * PAGE_SIZE)
#define SLUB_PAGE_SHIFT (PAGE_SHIFT + 2)
所以创建了从3到14位置大小分别为2^3,2^4….2^14大小mem_cache.注意到它们都是保存在数组kmalloc_cache[1-14]中的。即是它们是用于kmalloc的。
接下来的代码由于KMALLOC_MIN_SIZE为8 ,所以直到这里才会执行:
slab_state = UP;
/* Provide the correct
kmalloc names now that the caches are up */
for (i =
KMALLOC_SHIFT_LOW; i < SLUB_PAGE_SHIFT; i++)
kmalloc_caches[i].
name =
kasprintf(GFP_KERNEL,
"kmalloc-%d", 1 << i);
为每个kamlloc_caches[3-13]分配姓名。
kmem_size =
sizeof(struct kmem_cache);
计算大小,并且赋值给全局静态变量。
至此初始化完成。
在kmem_cache_init中对它的调用如下:
create_kmalloc_cache(&kmalloc_caches[i],"kmalloc", 1
<< i, GFP_KERNEL);
static struct kmem_cache *create_kmalloc_cache(struct kmem_cache
*s,
const char
*name, int size, gfp_t gfp_flags)
{
unsigned int flags =
0;
if (gfp_flags &
SLUB_DMA)
flags =
SLAB_CACHE_DMA;
down_write(&slub_lock);
if
(!kmem_cache_open(s, gfp_flags, name, size, ARCH_KMALLOC_MINALIGN,
flags,
NULL))
goto panic;
list_add(&s->list,
&slab_caches);
up_write(&slub_lock);
if (sysfs_slab_add(s))
goto panic;
return s;
首先判断gfp_flags是否设置了SLUB_DMA位,如果设置,则将flags设置成SLAB_CACHE_DMA;
然后进入kmem_cache_open函数。之后再将上面传递来的s,即kmalloc_caches[i]添加到链表slab_caches上。
static
LIST_HEAD(slab_caches);
然后调用sysfs_slab_add(s)将kmalloc_caches[i]添加到sysfs中。
static int kmem_cache_open(struct kmem_cache *s, gfp_t gfpflags,
const char
*name, size_t size,
size_t align,
unsigned long flags,
void
(*ctor)(void *))
{
memset(s, 0,
kmem_size);
s->name = name;
s->ctor = ctor;
s->objsize = size;
s->align = align;
s->flags =
kmem_cache_flags(size, flags, name, ctor);
if
(!calculate_sizes(s, -1))
goto error;
首先,将kmalloc_caches[i]清0;然后设定名字。构造函数NULL,容纳的对象大小,对齐大小。并且将flag赋值给kmalloc_cahes[i]的flags.
注意在
kmem_cache_open(s, gfp_flags,
name, size, ARCH_KMALLOC_MINALIGN, flags,
NULL)
的调用中,flags仅仅当为DMA时为SLAB_CACHE_DMA,其它情况全为0,即如果不是DMA,则s->flags=0;
然后调用calculate_sizes,calculate_sizes()
determines the order and the distribution of data within a slab object.
它主要完成以下的工作:
s-> = ;
s-> = ;
s-> = (order, );
s-> = ((), );
计算了对象数,对象的offset。对象size。可能还会设置flags,offset。
然后:
set_min_partial(s, ilog2(s->size));
s->refcount = 1;
设置了s->min_partial,和引用计数。
if
(!init_kmem_cache_nodes(s, gfpflags & ~SLUB_DMA))
goto error;
if
(alloc_kmem_cache_cpus(s, gfpflags & ~SLUB_DMA))
return 1;
free_kmem_cache_nodes(s);
之后三个函数,一个一个看。
在非#ifdef CONFIG_NUMA系统上。
static int init_kmem_cache_nodes(struct kmem_cache *s, gfp_t
gfpflags)
{
init_kmem_cache_node(&s->local_node,
s);
return 1;
}
static void
init_kmem_cache_node(struct kmem_cache_node *n, struct kmem_cache
*s)
{
n->nr_partial = 0;
spin_lock_init(&n->list_lock);
INIT_LIST_HEAD(&n->partial);
}
可以看到,仅仅是简单的赋值,初始化lock,初始化partial链表。
对于非对于多处理器即未定义#ifdef CONFIG_SMP
static inline int alloc_kmem_cache_cpus(struct kmem_cache *s,
gfp_t flags)
{
init_kmem_cache_cpu(s,
&s->cpu_slab);
return 1;
}
static void init_kmem_cache_cpu(struct kmem_cache *s,
struct
kmem_cache_cpu *c)
{
c->page = NULL;
c->freelist = NULL;
c->node = 0;
c->offset =
s->offset / sizeof(void *);
c->objsize =
s->objsize;
}
在slub_def.h中有如下定义:
#ifdef CONFIG_SMP
struct kmem_cache_cpu
*cpu_slab[NR_CPUS];
#else
struct kmem_cache_cpu
cpu_slab;
free_kmem_cache_nodes
static void free_kmem_cache_nodes(struct kmem_cache *s)
{
}
tatic __always_inline void *kmalloc(size_t size, gfp_t flags)
{
void *ret;
if
(__builtin_constant_p(size)) {
if (size >
SLUB_MAX_SIZE)
return
kmalloc_large(size, flags);
首先检查size是否为builtin,如果是,比如kalloc(100,KERNEL);
判断是否大于SLUB_MAX_SIZE;
#define SLUB_MAX_SIZE (2 * PAGE_SIZE)
即分配超过2个page的大小则使用kmalloc_large();
if (!(flags
& SLUB_DMA)) {
struct
kmem_cache *s = kmalloc_slab(size);
if (!s)
return
ZERO_SIZE_PTR;
ret =
kmem_cache_alloc_notrace(s, flags);
trace_kmalloc(_THIS_IP_,
ret, size, s->size, flags);
return
ret;
}
}
return __kmalloc(size,
flags);
}
如果flags不是SLUB_DMA,调用kmalloc_slab(size)
static __always_inline struct kmem_cache *kmalloc_slab(size_t
size)
{
int index =
kmalloc_index(size);
if (index == 0)
return NULL;
return
&kmalloc_caches[index];
}
即根据size的大小,对应的区间来找前面注册的kmem_caches即kmalloc_caches[i];比如是size=100;则index=7,所以kmalloc_slab返回kmalloc_caches[7];
然后:
static __always_inline void *
kmem_cache_alloc_notrace(struct kmem_cache *s, gfp_t gfpflags)
{
return
kmem_cache_alloc(s, gfpflags);
}
void *kmem_cache_alloc(struct kmem_cache *s, gfp_t gfpflags)
{
void *ret =
slab_alloc(s, gfpflags, -1, _RET_IP_);
trace_kmem_cache_alloc(_RET_IP_,
ret, s->objsize, s->size, gfpflags);
return ret;
}
进入真正的slab分配函数:
static __always_inline void *slab_alloc(struct kmem_cache *s,
gfp_t gfpflags,
int node, unsigned long addr)
{
void **object;
struct kmem_cache_cpu
*c;
unsigned long flags;
unsigned int objsize;
lockdep_trace_alloc(gfpflags);
might_sleep_if(gfpflags
& __GFP_WAIT);
if
(should_failslab(s->objsize, gfpflags))
return NULL;
local_irq_save(flags);
c = get_cpu_slab(s,
smp_processor_id());
objsize =
c->objsize;
做一些检查,关中断,取出per_cpu的struct kmem_cache_cpu cpu_slab;在alloc_kmem_caches中已经初始化过。然后取出objsize;
if (unlikely(!c->freelist || !node_match(c, node)))
object =
__slab_alloc(s, gfpflags, node, addr, c);
else {
object =
c->freelist;
c->freelist
= object[c->offset];
stat(c,
ALLOC_FASTPATH);
}
local_irq_restore(flags);
if (unlikely((gfpflags
& __GFP_ZERO) && object))
memset(object,
0, objsize);
return object;
}
如果freelist空闲队列木有可分配的了,或者node_match(c,node)注意,这个node只在NUMA中有用。
则调用__slab_alloc进行分配。
否则:
c->offset = s->offset / sizeof(void *);
s->offset=0;
结合人代码来看:
struct kmem_cache_cpu {
void **freelist; /* Pointer to first free per cpu object
*/
struct page *page; /* The slab from which we are allocating */
int node; /* The node of the page (or -1 for debug)
*/
unsigned int offset; /* Freepointer offset (in word units) */
unsigned int objsize; /* Size of an object (from kmem_cache) */
};
freelist指向第一个空闲的object.它相当于一个指针数组,所以取出指针以后,将它指向下一个object的位置。
然后恢复中断。如果设置了清0标志,则将这个object清0;
如果c->freelist为空,则会执行object = __slab_alloc(s, gfpflags, node, addr, c);
static void *__slab_alloc(struct kmem_cache *s, gfp_t gfpflags,
int node,
unsigned long addr, struct kmem_cache_cpu *c)
{
void **object;
struct page *new;
/* We handle
__GFP_ZERO in the caller */
gfpflags &=
~__GFP_ZERO;
if (!c->page)
goto new_slab;
slab_lock(c->page);
if
(unlikely(!node_match(c, node)))
goto
another_slab;
stat(c, ALLOC_REFILL);
清除了GFP_ZERO标志,因为在上面处理了。如果当前cpu的page为空,即未映射到内在页。则跳转到new_slab去执行。如果不match(c,node)会跳转到another_slab,由于非num中,始终为1,所以不会跳转 。
先看new_slab.
new_slab:
new = get_partial(s,
gfpflags, node);
if (new) {
c->page =
new;
stat(c,
ALLOC_FROM_PARTIAL);
goto
load_freelist;
}
先调用get_partial取出部分使用的page,赋值给new;如果存在,则c->page=new;然后跳转到load_freelist去。
若没有找到,即new=NULL;
if (gfpflags & __GFP_WAIT)
local_irq_enable();
new = new_slab(s,
gfpflags, node);
if (gfpflags &
__GFP_WAIT)
local_irq_disable();
if (new) {
c =
get_cpu_slab(s, smp_processor_id());
stat(c,
ALLOC_SLAB);
if (c->page)
flush_slab(s,
c);
slab_lock(new);
__SetPageSlubFrozen(new);
c->page =
new;
goto
load_freelist;
}
return NULL;
如果设置了wait位,则先开启中断。然后调用new_slab分配一个页面。如果分配成功,将这个per-cpu变量c的page变成NULL;然后将new赋值给c->page;最后跳转到load_freelist;
load_freelist:
object =
c->page->freelist;
if (unlikely(!object))
goto
another_slab;
if (unlikely(SLABDEBUG
&& PageSlubDebug(c->page)))
goto debug;
c->freelist =
object[c->offset];
c->page->inuse =
c->page->objects;
c->page->freelist
= NULL;
c->node =
page_to_nid(c->page);
unlock_out:
slab_unlock(c->page);
stat(c,
ALLOC_SLOWPATH);
return object;
static struct page *(struct *s, flags, int node)
1112 {
1113 struct *;
1114 void *;
1115 void *;
1116 void *p;
1117
1118 (flags & );
1119
1120 page = (s,
1121 flags &
( | ), node);
1122 if (!page)
1123 goto ;
1124
1125 inc_slabs_node(s, (page),
page->objects);
首先调用allocate_slab分配了一个page。inc_slabs_node在增加计数。
1126 page-> = s;
1127 page->flags |= 1
<< ;
1128 if (s->flags & ( | | |
1129 | ))
1130 (page);
将page结构体中的slab指针指向s。设置page的flag的PG_SLAG位。
1132 start = (page);
1133
1134 if ((s->flags & ))
1135 (start, , << (page));
1136
1137 = start;
1138 (p, s, start, page->objects) {
1139 (s, page, );
1140 (s, , p);
1141 = p;
1142 }
setup_object(s, page, last);
set_freepointer(s, last, NULL);
1147 page-> = 0;
1148 :
1149 return page;
取出这个page的地址start。
然后进入for_each_object:
282 #define (, , , ) \
283 for ( = (); < () + () * ()->;\
284 += ()->)
代入参数:
for(p=start;p < start+(page->objects)
* s->size;p+=s->size)
{
*(void **)(last + s->) =p;
last=p;
}
并且由以前的分析可知,此时s->offset=0;
其中page->objects在kmem_cache_open函数的calculate_sizes函数中已经计算,会在
假设object size为8,page的起始地址为0x0000情况如下:
addr content
0x0000 0x00008
0x0001 data
…….. data
0x0008 0x0016
…….. data
0x0016 0x0024
………………..
并且设置last为NULL;
这下再回到__slab_alloc的load_freelist中:
load_freelist:
object =
c->page->freelist;
if (unlikely(!object))
goto
another_slab;
if (unlikely(SLABDEBUG
&& PageSlubDebug(c->page)))
goto debug;
c->freelist =
object[c->offset];
c->page->inuse =
c->page->objects;
c->page->freelist
= NULL;
c->node =
page_to_nid(c->page);
unlock_out:
slab_unlock(c->page);
stat(c,
ALLOC_SLOWPATH);
return object;
可知:object=c->page->freelist=start=0x0000;
c->freelist=object[0]=0x0008;即它指向了下一个object;
然后返回这个0x0000作为分配到的object。
一开始我认为这要返回的话,一旦往里面写数据,肯定会把这个object里的地址给覆盖的。所以猜想,这个信息会在free时重建。
void
kfree(const void *x)
{
struct page *page;
void *object = (void *)x;
trace_kfree(_RET_IP_, x);
if (unlikely(ZERO_OR_NULL_PTR(x)))
return;
page = virt_to_head_page(x);
if (unlikely(!PageSlab(page))) {
BUG_ON(!PageCompound(page));
put_page(page);
return;
}
slab_free(page->slab, page, object,
_RET_IP_);
}
static
__always_inline void slab_free(struct kmem_cache *s,
struct page *page, void *x,
unsigned long addr)
{
void **object = (void *)x;
struct kmem_cache_cpu *c;
unsigned long flags;
local_irq_save(flags);
c = get_cpu_slab(s, smp_processor_id());
debug_check_no_locks_freed(object,
c->objsize);
if (!(s->flags &
SLAB_DEBUG_OBJECTS))
debug_check_no_obj_freed(object,
c->objsize);
if (likely(page == c->page &&
c->node >= 0)) {
object[c->offset] =
c->freelist;
c->freelist = object;
stat(c, FREE_FASTPATH);
} else
__slab_free(s, page, x, addr,
c->offset);
local_irq_restore(flags);
}
addr content
0x0000 data
0x0001 data
…….. data
0x0008 data
…….. data
c->freelist 0x0016
0x0024
………………..
假如已经分配了两个obj即c->freelist=0x0016;
此时要free 第一个。
addr content
c->freelist 0x0000
0x00016
0x0001 data
…….. data
0x0008 data
…….. data
0x0016 0x0024
………………..
c->freelist=0x0000;
如果还得再free第二个object;
则object=(void *)x=0x0008;
object[0]=0x0000;
c->freelist=0x0008;
addr content
0x0000 0x00016
0x0001 data
…….. data
c->freelist 0x0008
0x0000
…….. data
0x0016 0x0024
………………..
chinaunix网友2010-08-23 18:32:49
假设object size为8,page的起始地址为0x0000情况如下: addr content 0x0000 0x00008 0x0001 data …….. data 0x0008 0x0016 …….. data 0x0016 0x0024 ==================== 以上错了。 在s->offset = 0的时候,slub是直接使用objsize的前8个字节存储下一个free pointer, 也就是说占用object的前8个字节。 因此object的size的8的时候,格式是这样的(x86_64): addr content 0x0000 0000 0000 0000 : 0x08 0x0000 0000 0000 0001 : 0x00 …….. 0x00 0x0000 0000 0000 0008 : 0x16