感谢支持
我们一直在努力

Linux高端内存管理之非连续内存区(分配和释放)

前面总结了非连续内存区域的内核描述,接着看看他的分配和释放。


相关阅读:


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?


  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?


  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()建立页表映射机制的实现就是依次对pgdpudpmdpte的设置。

二、非连续内存区的释放


调用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机制等部分需要再做分析和总结。

赞(0) 打赏
转载请注明出处:服务器评测 » Linux高端内存管理之非连续内存区(分配和释放)
分享到: 更多 (0)

听说打赏我的人,都进福布斯排行榜啦!

支付宝扫一扫打赏

微信扫一扫打赏