前面總結了非連續內存區域的內核描述,接著看看他的分配和釋放。
相關閱讀:
http://www.linuxidc.com/Linux/2012-02/53457.htm
http://www.linuxidc.com/Linux/2012-02/53458.htm
http://www.linuxidc.com/Linux/2012-02/53459.htm
http://www.linuxidc.com/Linux/2012-02/53460.htm
一、非連續內存區的分配
不管是vmalloc()還是vmalloc_32()等系列的分配函數最後都會調用__vmalloc_node()函數實現,直接看這個函數的實現。
[cpp] view plaincopyprint?
- * __vmalloc_node - allocate virtually contiguous memory
- * @size: allocation size
- * @align: desired alignment
- * @gfp_mask: flags for the page level allocator
- * @prot: protection mask for the allocated pages
- * @node: node to use for allocation or -1
- * @caller: caller's return address
- *
- * Allocate enough pages to cover @size from the page level
- * allocator with @gfp_mask flags. Map them into contiguous
- * kernel virtual space, using a pagetable protection of @prot.
- */
- static void *__vmalloc_node(unsigned long size, unsigned long align,
- 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);
- if (!size || (size >> PAGE_SHIFT) > totalram_pages)
- return NULL;
- /*分配相關的結構並對其初始化,在前面介紹過了*/
- area = __get_vm_area_node(size, align, VM_ALLOC, VMALLOC_START,
- VMALLOC_END, node, gfp_mask, caller);
-
- if (!area)
- return NULL;
- /*分配物理空間,建立頁表映射*/
- addr = __vmalloc_area_node(area, gfp_mask, prot, node, caller);
-
- /*
- * 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;
- }
[cpp] view plaincopyprint?
- struct page **pages;
- unsigned int nr_pages, array_size, i;
- /*需要減去一個頁面,因為在分配結構的時候指定了多一個頁面*/
- nr_pages = (area->size - PAGE_SIZE) >> PAGE_SHIFT;
- /*頁面指針所占空間大小*/
- array_size = (nr_pages * sizeof(struct 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, gfp_mask | __GFP_ZERO,
- PAGE_KERNEL, node, caller);
- area->flags |= VM_VPAGES;
- } else {/*如果頁面指針空間所占大小��於一個頁面時,用slab機制分配這個空間*/
- pages = kmalloc_node(array_size,
- (gfp_mask & GFP_RECLAIM_MASK) | __GFP_ZERO,
- node);
- }
- /*初始化area結構*/
- 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;
-
- if (node < 0)/*分配物理頁面空間*/
- page = alloc_page(gfp_mask);
- else
- page = alloc_pages_node(node, gfp_mask, 0);
-
- if (unlikely(!page)) {
- /* Successfully allocated i pages, free them in __vunmap() */
- area->nr_pages = i;
- goto fail;
- }
- area->pages[i] = page;/*初始化area中page數組*/
- }
- /*因為非連續區間沒有建立頁表機制,在這裡需要建立他*/
- if (map_vm_area(area, prot, &pages))
- goto fail;
- return area->addr;/*返回線性地址*/
-
- fail:
- vfree(area->addr);
- return NULL;
- }
其中map_vm_area()建立頁表映射機制的實現就是依次對pgd、pud、pmd、pte的設置。