static __always_inline void *kmalloc(size_t size, gfp_t flags)
{
struct kmem_cache *cachep;
void *ret;
if (__builtin_constant_p(size)) { //__builtin_constant_p 是编译器gcc内置函数,用于判断一个值是否为编译时常量,如果是常数,返回1,否则返回0
int i = 0;
if (!size)
return ZERO_SIZE_PTR;
#define CACHE(x) \
if (size <= x) \
goto found; \
else \
i++;
#include //这里查询申请的size在哪个范围 从32乘2递增
#undef CACHE
return NULL;
found:
#ifdef CONFIG_ZONE_DMA
if (flags & GFP_DMA) //如果定义了dma,并且设置了dma标志则优先从dma_cache了申请。malloc_sizes的初始化在slab.c里
cachep = malloc_sizes[i].cs_dmacachep;
else
#endif
cachep = malloc_sizes[i].cs_cachep; //从指定的cache链表分配内存,不浪费空间。
ret = kmem_cache_alloc_trace(size, cachep, flags);
return ret;
}
return __kmalloc(size, flags);
}
kmem_cache_alloc_trace(size_t size, struct kmem_cache *cachep, gfp_t flags)
{
return kmem_cache_alloc(cachep, flags); //调用slab_alloc
}
void *kmem_cache_alloc(struct kmem_cache *s, gfp_t gfpflags)
{
void *ret = slab_alloc(s, gfpflags, NUMA_NO_NODE, _RET_IP_);
trace_kmem_cache_alloc(_RET_IP_, ret, s->objsize, s->size, gfpflags); //跟踪调试会用到
return ret;
}
kmalloc最后主要调用了slab,下面看看
//
#if (PAGE_SIZE == 4096)
CACHE(32)
#endif
CACHE(64)
#if L1_CACHE_BYTES < 64
CACHE(96)
#endif
CACHE(128)
#if L1_CACHE_BYTES < 128
CACHE(192)
#endif
CACHE(256)
CACHE(512)
CACHE(1024)
CACHE(2048)
CACHE(4096)
CACHE(8192)
CACHE(16384)
CACHE(32768)
CACHE(65536)
CACHE(131072)
#if KMALLOC_MAX_SIZE >= 262144
CACHE(262144)
#endif
#if KMALLOC_MAX_SIZE >= 524288
CACHE(524288)
#endif
#if KMALLOC_MAX_SIZE >= 1048576
CACHE(1048576)
#endif
#if KMALLOC_MAX_SIZE >= 2097152
CACHE(2097152)
#endif
#if KMALLOC_MAX_SIZE >= 4194304
CACHE(4194304)
#endif
#if KMALLOC_MAX_SIZE >= 8388608
CACHE(8388608)
#endif
#if KMALLOC_MAX_SIZE >= 16777216
CACHE(16777216)
#endif
#if KMALLOC_MAX_SIZE >= 33554432
CACHE(33554432)
#endif
void *vmalloc(unsigned long size)
{
return __vmalloc_node_flags(size, -1, GFP_KERNEL | __GFP_HIGHMEM);
}
static inline void *__vmalloc_node_flags(unsigned long size,
int node, gfp_t flags)
{
return __vmalloc_node(size, 1, flags, PAGE_KERNEL,
node, __builtin_return_address(0));
}
static void *__vmalloc_node(unsigned long size, unsigned long align,
gfp_t gfp_mask, pgprot_t prot,
int node, void *caller)
{
return __vmalloc_node_range(size, align, VMALLOC_START, VMALLOC_END,
gfp_mask, prot, node, caller);
}
void *__vmalloc_node_range(unsigned long size, unsigned long align,
unsigned long start, unsigned long end, gfp_t gfp_mask,
pgprot_t prot, int node, void *caller)
{
struct vm_struct *area;
void *addr;
unsigned long real_size = size;
size = PAGE_ALIGN(size); //对size进行页面对齐设置
if (!size || (size >> PAGE_SHIFT) > totalram_pages) //检测size合法性
goto fail;
area = __get_vm_area_node(size, align, VM_ALLOC | VM_UNLIST, //在高端内存区分配一个vm_struct并初始化
start, end, node, gfp_mask, caller);
if (!area)
goto fail;
addr = __vmalloc_area_node(area, gfp_mask, prot, node, caller); //为area分配管理page的数组,并通过伙伴系统分配物理页面
if (!addr) //上面可以看出来,高端内存在分配时就分配物理内存,用户空间等待访问时才分配物理内存
return NULL;
/*
* In this function, newly allocated vm_struct is not added
* to vmlist at __get_vm_area_node(). so, it is added here.
*/
insert_vmalloc_vmlist(area);
/*
* A ref_count = 3 is needed because the vm_struct and vmap_area
* structures allocated in the __get_vm_area_node() function contain
* references to the virtual address of the vmalloc'ed block.
*/
kmemleak_alloc(addr, real_size, 3, gfp_mask);
return addr;
fail:
warn_alloc_failed(gfp_mask, 0,
"vmalloc: allocation failure: %lu bytes\n",
real_size);
return NULL;
}
上述函数主要调用了__get_vm_area_node,与__vmalloc_area_node函数,实现分配vm_struct以及分配物理页面,下面分析这两个函数:
static struct vm_struct *__get_vm_area_node(unsigned long size,
unsigned long align, unsigned long flags, unsigned long start,
unsigned long end, int node, gfp_t gfp_mask, void *caller)
{
struct vmap_area *va;
struct vm_struct *area;
BUG_ON(in_interrupt()); //不能用于中断上下文
if (flags & VM_IOREMAP) { //物理页面的获取并不能保证可以立刻得到,如果页面不足会导致休眠
int bit = fls(size); //所以不能用于中断上下文
if (bit > IOREMAP_MAX_ORDER)
bit = IOREMAP_MAX_ORDER;
else if (bit < PAGE_SHIFT)
bit = PAGE_SHIFT;
align = 1ul << bit;
}
size = PAGE_ALIGN(size); //对size 进行对齐
if (unlikely(!size))
return NULL;
area = kzalloc_node(sizeof(*area), gfp_mask & GFP_RECLAIM_MASK, node); //分配一个vm_alloc结构
if (unlikely(!area))
return NULL;
/*
* We always allocate a guard page.
*/
size += PAGE_SIZE; //size加了一个page的大小主要做各个区间之间的隔离
va = alloc_vmap_area(size, align, start, end, node, gfp_mask); //分配一块虚拟内存地址空间
if (IS_ERR(va)) { //上面与用户空间中的malloc相似,仅仅分配地址空间
kfree(area);
return NULL;
}
/*
* When this function is called from __vmalloc_node_range,
* we do not add vm_struct to vmlist here to avoid
* accessing uninitialized members of vm_struct such as
* pages and nr_pages fields. They will be set later.
* To distinguish it from others, we use a VM_UNLIST flag.
*/
if (flags & VM_UNLIST)
setup_vmalloc_vm(area, va, flags, caller); //初始化area
else
insert_vmalloc_vm(area, va, flags, caller);
return area;
}
static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
pgprot_t prot, int node, void *caller)
{
const int order = 0;
struct page **pages;
unsigned int nr_pages, array_size, i;
gfp_t nested_gfp = (gfp_mask & GFP_RECLAIM_MASK) | __GFP_ZERO;
nr_pages = (area->size - PAGE_SIZE) >> PAGE_SHIFT; //需要分配的页面数量
array_size = (nr_pages * sizeof(struct page *)); //需要一个数组管理涉及到的物理页面地址即page指针,这个数组的大小
area->nr_pages = nr_pages;
/* Please note that the recursion is strictly bounded. */
if (array_size > PAGE_SIZE) { //如果数组的大小大于一个页面,则在高端内存中分配
pages = __vmalloc_node(array_size, 1, nested_gfp|__GFP_HIGHMEM,
PAGE_KERNEL, node, caller);
area->flags |= VM_VPAGES;
} else {
pages = kmalloc_node(array_size, nested_gfp, node); //page指针数组的大小小于一个页面的大小,则直接在线性映射区直接分配,更快
}
area->pages = pages;
area->caller = caller;
if (!area->pages) {
remove_vm_area(area->addr);
kfree(area);
return NULL;
}
for (i = 0; i < area->nr_pages; i++) { //进入循环,为每一个虚拟页框分配物理页面
struct page *page; //从这里可以看出,高端内存的在虚拟区间是连续的,但是在物理地址空间却是离散的
gfp_t tmp_mask = gfp_mask | __GFP_NOWARN;
if (node < 0)
page = alloc_page(tmp_mask);
else
page = alloc_pages_node(node, tmp_mask, order);
if (unlikely(!page)) {
/* Successfully allocated i pages, free them in __vunmap() */
area->nr_pages = i;
goto fail;
}
area->pages[i] = page;
}
if (map_vm_area(area, prot, &pages)) //为页面建立映射,就是填充页表
goto fail;
return area->addr;
fail:
warn_alloc_failed(gfp_mask, order,
"vmalloc: allocation failure, allocated %ld of %ld bytes\n",
(area->nr_pages*PAGE_SIZE), area->size);
vfree(area->addr);
return NULL;
}
从上面可以看出,高端内存的分配,每一个虚拟页分别分配一个物理页,虚拟内存分配时是连续的,但是在物理内存上并不是连续的。虚拟内存分配时调用alloc_vmap_area分配的是一块内存,物理内存分配时是分别按页分配的,不是整块分配的。
alloc_page就是分配页,上一节中有讲到。
通过上面源码分析可以看出:
kmalloc是建立在slab分配器的基础上的,分配在线性映射区中。虚拟空间与物理内存空间都连续。
vmalloc最终是调用alloc_page实现的,分配在高端内存中,虚拟内存连续,但是物理内存空间不连续。