分类: LINUX
2012-05-16 10:31:01
前面总结了非连续内存区域的内核描述,接着看看他的分配和释放。
一、非连续内存区的分配
不管是vmalloc()还是vmalloc_32()等系列的分配函数最后都会调用__vmalloc_node()函数实现,直接看这个函数的实现。
[cpp] view plaincopyprint?
1. * __vmalloc_node - allocate virtually contiguous memory
2. * @size: allocation size
3. * @align: desired alignment
4. * @gfp_mask: flags for the page level allocator
5. * @prot: protection mask for the allocated pages
6. * @node: node to use for allocation or -1
7. * @caller: caller's return address
8. *
9. * Allocate enough pages to cover @size from the page level
10. * allocator with @gfp_mask flags. Map them into contiguous
11. * kernel virtual space, using a pagetable protection of @prot.
12. */
13. static void *__vmalloc_node(unsigned long size, unsigned long align,
14. gfp_t gfp_mask, pgprot_t prot,
15. int node, void *caller)
16. {
17. struct vm_struct *area;
18. void *addr;
19. unsigned long real_size = size;
20.
21. size = PAGE_ALIGN(size);
22. if (!size || (size >> PAGE_SHIFT) > totalram_pages)
23. return NULL;
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. return NULL;
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. return addr;
42. }
[cpp] view plaincopyprint?
static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask, pgprot_t prot, int node, void *caller)
1. struct page **pages;
2. unsigned int nr_pages, array_size, i;
3. /*需要减去一个页面,因为在分配结构的时候指定了多一个页面*/
4. nr_pages = (area->size - PAGE_SIZE) >> PAGE_SHIFT;
5. /*页面指针所占空间大小*/
6. array_size = (nr_pages * sizeof(struct page *));
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. return NULL;
26. }
27. /*对每个页面调用分配函数分配物理空间,
28. 也就是每次分配一个页面*/
29. for (i = 0; i < area->nr_pages; i++) {
30. struct page *page;
31.
32. if (node < 0)/*分配物理页面空间*/
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. goto fail;
41. }
42. area->pages[i] = page;/*初始化area中page数组*/
43. }
44. /*因为非连续区间没有建立页表机制,在这里需要建立他*/
45. if (map_vm_area(area, prot, &pages))
46. goto fail;
47. return area->addr;/*返回线性地址*/
48.
49. fail:
50. vfree(area->addr);
51. return NULL;
52. }
其中map_vm_area()建立页表映射机制的实现就是依次对pgd、pud、pmd、pte的设置。
二、非连续内存区的释放
调用vfree()函数实现
[cpp] view plaincopyprint?
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. void vfree(const void *addr)
12. {
13. BUG_ON(in_interrupt());
14. /*调试用*/
15. kmemleak_free(addr);
16. /*释放工作*/
17. __vunmap(addr, 1);
18. }
[cpp] view plaincopyprint?
1. static void __vunmap(const void *addr, int deallocate_pages)
2. {
3. struct vm_struct *area;
4.
5. if (!addr)
6. return;
7.
8. if ((PAGE_SIZE-1) & (unsigned long)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. int i;
25.
26. for (i = 0; i < area->nr_pages; i++) {/*每次释放一个页面*/
27. struct page *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. }
[cpp] view plaincopyprint?
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. struct vm_struct *remove_vm_area(const void *addr)
10. {
11. struct vmap_area *va;
12. /*从红黑树种查找而不是链表,为了效率起见*/
13. va = find_vmap_area((unsigned long)addr);
14. if (va && va->flags & VM_VM_AREA) {
15. struct vm_struct *vm = va->private;
16. struct vm_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. return vm;
35. }
36. return NULL;
37. }
总结:linux高端内存非连续区的整体描述以及其分配和释放基本就总结完了。总结的只是一个大概的原理框架,不过根据这个框架对细节的了解应该不难。另外,其中涉及到伙伴系统、slab机制等部分需要再做分析和总结。