内存分配的API有:alloc_pages, kmalloc, slab分配,vmalloc, malloc,下面简单说明一下以上几个函数的差别:
alloc_pages:主要用来从buddy系统中分配内存,以页为单位分配区域需要通过gfp_flag指定,一般情况下会优先从highmem zone分配,返回的是分配到物理首个页面的管理结构体struct page,buddy里面页面的组织情况如下图:
alloc_pages返回的是满足条件的用于管理free_area中对应大小的block的page struct的地址,同时每个free_area元素的migrationtype中的内存block的管理者都通过page组成的lru链表管理,而alloc_pages返回的是通过list_entry来获取这个lru链表中的一个page struct返回,从管理页面的角度,buddy可以化成如下示意图:
而alloc_pages如果分配的order是小于等于4,则返回的是上图中其中一个page struct
kmalloc和slab:kmalloc是slab的一个通用分配内存API,slab分配器从buddy拿到需求大小且物理内存连续的物理页面个数,然后kmalloc再从slab分配器中获取需要的slab对象,slab对象大小可以为64,128,256等等个字节数,相对于专用的slab分配内存主要是部分驱动申请的slab描述符,然后再进行slab对象的内存申请。此时申请的物理内存是连续的
vmalloc:再kernel space的vmalloc area中申请内存,并建立虚拟地址到物理地址映射,此时的物理地址可能是不连续的,因为在申请内存时时一个页面一个页面alloc_pages,后面建立映射过程中也是一个页面一个页面映射。
static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
pgprot_t prot, int node)
{
………………
for (i = 0; i < area->nr_pages; i++) {
struct page *page;
if (node == NUMA_NO_NODE)
page = alloc_page(alloc_mask);
else
page = alloc_pages_node(node, alloc_mask, order);
if (unlikely(!page)) {
/* Successfully allocated i pages, free them in __vunmap() */
area->nr_pages = i;
goto fail;
}
area->pages[i] = page;
if (gfp_mask & __GFP_WAIT)
cond_resched();
}
………………
}
static int vmap_pte_range(pmd_t *pmd, unsigned long addr,
unsigned long end, pgprot_t prot, struct page **pages, int *nr)
{
………………
do {
struct page *page = pages[*nr];
if (WARN_ON(!pte_none(*pte)))
return -EBUSY;
if (WARN_ON(!page))
return -ENOMEM;
set_pte_at(&init_mm, addr, pte, mk_pte(page, prot));
(*nr)++;
} while (pte++, addr += PAGE_SIZE, addr != end);
return 0;
}
malloc:主要是在进程的虚拟地址空间user space中申请vma,但是此时并没有分配物理内存,除分配flag带有VM_MLOCK标志需要立即建立虚拟地址到物理地址的映射外,其他都是在进程需要访问此虚拟内存时,通过缺页中断来申请的物理地址,并建立映射,另外malloc申请到的物理内存也是不连续的。
mmap:在私有内存映射中,当malloc申请的内存size大于128KB时,glibc会自动使用mmap来代替brk分配内存。