前面总结了非连续内存区域的内核描述,接着看看他的分配和释放。
一、非连续内存区的分配
不管是vmalloc()还是vmalloc_32()等系列的分配函数最后都会调用__vmalloc_node()函数实现,直接看这个函数的实现。
1.* __vmalloc_node - allocate virtually contiguous memory
2.* @size: allocation size
3.* @align: desired alignment
4.* @gfp_mask: flagsforthe page level allocator
5.* @prot: protection maskforthe allocated pages
6.* @node: node to useforallocation or -1
7.* @caller: caller'sreturnaddress
8.*
9.* Allocate enough pages to cover @size from the page level
10.* allocator with @gfp_mask flags. Map them into contiguous
11.* kernelvirtualspace,usinga pagetable protection of @prot.
12.*/
13.staticvoid*__vmalloc_node(unsignedlongsize, unsignedlongalign,
14.gfp_t gfp_mask, pgprot_t prot,
15.intnode,void*caller)
16.{
17.structvm_struct *area;
18.void*addr;
19.unsignedlongreal_size = size;
20.
21.size = PAGE_ALIGN(size);
22.if(!size || (size >> PAGE_SHIFT) > totalram_pages)
23.returnNULL;
24./*分配相关的结构并对其初始化,在前面介绍过了*/
25.area = __get_vm_area_node(size, align, VM_ALLOC, VMALLOC_START,
26.VMALLOC_END, node, gfp_mask, caller);
27.
28.if(!area)
29.returnNULL;
30./*分配物理空间,建立页表映射*/
31.addr = __vmalloc_area_node(area, gfp_mask, prot, node, caller);
32.
33./*
34.* A ref_count = 3 is needed because the vm_struct and vmap_area
35.* structures allocated in the __get_vm_area_node() function contain
36.* references to the virtual address of the vmalloc'ed block.
37.*/
38./*调试用*/
39.kmemleak_alloc(addr, real_size, 3, gfp_mask);
40.
41.returnaddr;
42.}
static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
pgprot_t prot, int node, void *caller)
1.structpage **pages;
2.unsignedintnr_pages, array_size, i;
3./*需要减去一个页面,因为在分配结构的时候指定了多一个页面*/
4.nr_pages = (area->size - PAGE_SIZE) >> PAGE_SHIFT;
5./*页面指针所占空间大小*/
6.array_size = (nr_pages *sizeof(structpage *));
7.
8.area->nr_pages = nr_pages;
9./* Please note that the recursion is strictly bounded. */
10.if(array_size > PAGE_SIZE) {/*如果页面指针空间大于一个页面时,这个空间用非连续内存分配*/
11.pages = __vmalloc_node(array_size, 1, gfp_mask | __GFP_ZERO,
12.PAGE_KERNEL, node, caller);
13.area->flags |= VM_VPAGES;
14.}else{/*如果页面指针空间所占大小小于一个页面时,用slab机制分配这个空间*/
15.pages = kmalloc_node(array_size,
16.(gfp_mask & GFP_RECLAIM_MASK) | __GFP_ZERO,
17.node);
18.}
19./*初始化area结构*/
20.area->pages = pages;
21.area->caller = caller;
22.if(!area->pages) {
23.remove_vm_area(area->addr);
24.kfree(area);
25.returnNULL;
26.}
27./*对每个页面调用分配函数分配物理空间,
28.也就是每次分配一个页面*/
29.for(i = 0; i nr_pages; i++) {
30.structpage *page;
31.
32.if(node /*分配物理页面空间*/
33.page = alloc_page(gfp_mask);
34.else
35.page = alloc_pages_node(node, gfp_mask, 0);
36.
37.if(unlikely(!page)) {
38./* Successfully allocated i pages, free them in __vunmap() */
39.area->nr_pages = i;
40.gotofail;
41.}
42.area->pages[i] = page;/*初始化area中page数组*/
43.}
44./*因为非连续区间没有建立页表机制,在这里需要建立他*/
45.if(map_vm_area(area, prot, &pages))
46.gotofail;
47.returnarea->addr;/*返回线性地址*/
48.
49.fail:
50.vfree(area->addr);
51.returnNULL;
52.}
其中map_vm_area()建立页表映射机制的实现就是依次对pgd、pud、pmd、pte的设置。
二、非连续内存区的释放
调用vfree()函数实现
1./**
2.* vfree - release memory allocated by vmalloc()
3.* @addr: memory base address
4.*
5.* Free the virtually continuous memory area starting at @addr, as
6.* obtained from vmalloc(), vmalloc_32() or __vmalloc(). If @addr is
7.* NULL, no operation is performed.
8.*
9.* Must not be called in interrupt context.
10.*/
11.voidvfree(constvoid*addr)
12.{
13.BUG_ON(in_interrupt());
14./*调试用*/
15.kmemleak_free(addr);
16./*释放工作*/
17.__vunmap(addr, 1);
18.}
1.staticvoid__vunmap(constvoid*addr,intdeallocate_pages)
2.{
3.structvm_struct *area;
4.
5.if(!addr)
6.return;
7.
8.if((PAGE_SIZE-1) & (unsignedlong)addr) {
9.WARN(1, KERN_ERR"Trying to vfree() bad address (%p)\n", addr);
10.return;
11.}
12./*从vlist链表和红黑树中移除指定地址的线性区间*/
13.area = remove_vm_area(addr);
14.if(unlikely(!area)) {
15.WARN(1, KERN_ERR"Trying to vfree() nonexistent vm area (%p)\n",
16.addr);
17.return;
18.}
19.
20.debug_check_no_locks_freed(addr, area->size);
21.debug_check_no_obj_freed(addr, area->size);
22.
23.if(deallocate_pages) {
24.inti;
25.
26.for(i = 0; i nr_pages; i++) {/*每次释放一个页面*/
27.structpage *page = area->pages[i];
28.
29.BUG_ON(!page);
30.__free_page(page);
31.}
32.
33.if(area->flags & VM_VPAGES)/*在创建非连续区间时,如果页面
34.指针所占的空间大于一个页面时,从非连续内存区间
35.中分配。所以这里也就从相应的释放*/
36.vfree(area->pages);
37.else
38.kfree(area->pages);/*从slab中释放*/
39.}
40.
41.kfree(area);/*释放area*/
42.return;
43.}
1./**
2.* remove_vm_area - find and remove a continuous kernel virtual area
3.* @addr: base address
4.*
5.* Search for the kernel VM area starting at @addr, and remove it.
6.* This function returns the found VM area, but using it is NOT safe
7.* on SMP machines, except for its size or flags.
8.*/
9.structvm_struct *remove_vm_area(constvoid*addr)
10.{
11.structvmap_area *va;
12./*从红黑树种查找而不是链表,为了效率起见*/
13.va = find_vmap_area((unsignedlong)addr);
14.if(va && va->flags & VM_VM_AREA) {
15.structvm_struct *vm = va->private;
16.structvm_struct *tmp, **p;
17./*
18.* remove from list and disallow access to this vm_struct
19.* before unmap. (address range confliction is maintained by
20.* vmap.)
21.*/
22.write_lock(&vmlist_lock);
23./*从链表中找到,然后删除*/
24.for(p = &vmlist; (tmp = *p) != vm; p = &tmp->next)
25.;
26.*p = tmp->next;
27.write_unlock(&vmlist_lock);
28./*调试用*/
29.vmap_debug_free_range(va->va_start, va->va_end);
30./*从红黑树中删除*/
31.free_unmap_vmap_area(va);
32.vm->size -= PAGE_SIZE;
33.
34.returnvm;
35.}
36.returnNULL;
37.}
总结:linux高端内存非连续区的整体描述以及其分配和释放基本就总结完了。总结的只是一个大概的原理框架,不过根据这个框架对细节的了解应该不难。另外,其中涉及到伙伴系统、slab机制等部分需要再做分析和总结。