免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1336 | 回复: 1
打印 上一主题 下一主题

linux高端内存管理之非连续内存区(分配和释放) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2012-01-06 16:29 |只看该作者 |倒序浏览
linux高端内存管理之非连续内存区(分配和释放)






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

一、非连续内存区的分配

不管是vmalloc()还是vmalloc_32()等系列的分配函数最后都会调用__vmalloc_node()函数实现,直接看这个函数的实现。
  1. view plaincopy to clipboardprint? *  __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. }  
  43. *        __vmalloc_node  -  allocate virtually contiguous memory
  44. *        @size:                allocation size
  45. *        @align:                desired alignment
  46. *        @gfp_mask:        flags for the page level allocator
  47. *        @prot:                protection mask for the allocated pages
  48. *        @node:                node to use for allocation or -1
  49. *        @caller:        caller's return address
  50. *
  51. *        Allocate enough pages to cover @size from the page level
  52. *        allocator with @gfp_mask flags.  Map them into contiguous
  53. *        kernel virtual space, using a pagetable protection of @prot.
  54. */
  55. static void *__vmalloc_node(unsigned long size, unsigned long align,
  56.                             gfp_t gfp_mask, pgprot_t prot,
  57.                             int node, void *caller)
  58. {
  59.         struct vm_struct *area;
  60.         void *addr;
  61.         unsigned long real_size = size;

  62.         size = PAGE_ALIGN(size);
  63.         if (!size || (size >> PAGE_SHIFT) > totalram_pages)
  64.                 return NULL;
  65.         /*分配相关的结构并对其初始化,在前面介绍过了*/
  66.         area = __get_vm_area_node(size, align, VM_ALLOC, VMALLOC_START,
  67.                                   VMALLOC_END, node, gfp_mask, caller);

  68.         if (!area)
  69.                 return NULL;
  70.         /*分配物理空间,建立页表映射*/
  71.         addr = __vmalloc_area_node(area, gfp_mask, prot, node, caller);

  72.         /*
  73.          * A ref_count = 3 is needed because the vm_struct and vmap_area
  74.          * structures allocated in the __get_vm_area_node() function contain
  75.          * references to the virtual address of the vmalloc'ed block.
  76.          */
  77.          /*调试用*/
  78.         kmemleak_alloc(addr, real_size, 3, gfp_mask);

  79.         return addr;
  80. }view plaincopy to clipboardprint?    struct page **pages;  
  81.     unsigned int nr_pages, array_size, i;  
  82.     /*需要减去一个页面,因为在分配结构的时候指定了多一个页面*/  
  83.     nr_pages = (area->size - PAGE_SIZE) >> PAGE_SHIFT;  
  84.     /*页面指针所占空间大小*/  
  85.     array_size = (nr_pages * sizeof(struct page *));  
  86.   
  87.     area->nr_pages = nr_pages;  
  88.     /* Please note that the recursion is strictly bounded. */  
  89.     if (array_size > PAGE_SIZE) {/*如果页面指针空间大于一个页面时,这个空间用非连续内存分配*/   
  90.         pages = __vmalloc_node(array_size, 1, gfp_mask | __GFP_ZERO,  
  91.                 PAGE_KERNEL, node, caller);  
  92.         area->flags |= VM_VPAGES;  
  93.     } else {/*如果页面指针空间所占大小小于一个页面时,用slab机制分配这个空间*/  
  94.         pages = kmalloc_node(array_size,  
  95.                 (gfp_mask & GFP_RECLAIM_MASK) | __GFP_ZERO,  
  96.                 node);  
  97.     }  
  98.     /*初始化area结构*/  
  99.     area->pages = pages;  
  100.     area->caller = caller;  
  101.     if (!area->pages) {  
  102.         remove_vm_area(area->addr);  
  103.         kfree(area);  
  104.         return NULL;  
  105.     }  
  106.     /*对每个页面调用分配函数分配物理空间,
  107.     也就是每次分配一个页面*/  
  108.     for (i = 0; i < area->nr_pages; i++) {  
  109.         struct page *page;  
  110.   
  111.         if (node < 0)/*分配物理页面空间*/  
  112.             page = alloc_page(gfp_mask);  
  113.         else  
  114.             page = alloc_pages_node(node, gfp_mask, 0);  
  115.   
  116.         if (unlikely(!page)) {  
  117.             /* Successfully allocated i pages, free them in __vunmap() */  
  118.             area->nr_pages = i;  
  119.             goto fail;  
  120.         }  
  121.         area->pages[i] = page;/*初始化area中page数组*/  
  122.     }  
  123.     /*因为非连续区间没有建立页表机制,在这里需要建立他*/  
  124.     if (map_vm_area(area, prot, &pages))  
  125.         goto fail;  
  126.     return area->addr;/*返回线性地址*/  
  127.   
  128. fail:  
  129.     vfree(area->addr);  
  130.     return NULL;  
  131. }  
  132.         struct page **pages;
  133.         unsigned int nr_pages, array_size, i;
  134.         /*需要减去一个页面,因为在分配结构的时候指定了多一个页面*/
  135.         nr_pages = (area->size - PAGE_SIZE) >> PAGE_SHIFT;
  136.         /*页面指针所占空间大小*/
  137.         array_size = (nr_pages * sizeof(struct page *));

  138.         area->nr_pages = nr_pages;
  139.         /* Please note that the recursion is strictly bounded. */
  140.         if (array_size > PAGE_SIZE) {/*如果页面指针空间大于一个页面时,这个空间用非连续内存分配*/
  141.                 pages = __vmalloc_node(array_size, 1, gfp_mask | __GFP_ZERO,
  142.                                 PAGE_KERNEL, node, caller);
  143.                 area->flags |= VM_VPAGES;
  144.         } else {/*如果页面指针空间所占大小小于一个页面时,用slab机制分配这个空间*/
  145.                 pages = kmalloc_node(array_size,
  146.                                 (gfp_mask & GFP_RECLAIM_MASK) | __GFP_ZERO,
  147.                                 node);
  148.         }
  149.         /*初始化area结构*/
  150.         area->pages = pages;
  151.         area->caller = caller;
  152.         if (!area->pages) {
  153.                 remove_vm_area(area->addr);
  154.                 kfree(area);
  155.                 return NULL;
  156.         }
  157.         /*对每个页面调用分配函数分配物理空间,
  158.         也就是每次分配一个页面*/
  159.         for (i = 0; i < area->nr_pages; i++) {
  160.                 struct page *page;

  161.                 if (node < 0)/*分配物理页面空间*/
  162.                         page = alloc_page(gfp_mask);
  163.                 else
  164.                         page = alloc_pages_node(node, gfp_mask, 0);

  165.                 if (unlikely(!page)) {
  166.                         /* Successfully allocated i pages, free them in __vunmap() */
  167.                         area->nr_pages = i;
  168.                         goto fail;
  169.                 }
  170.                 area->pages[i] = page;/*初始化area中page数组*/
  171.         }
  172.         /*因为非连续区间没有建立页表机制,在这里需要建立他*/
  173.         if (map_vm_area(area, prot, &pages))
  174.                 goto fail;
  175.         return area->addr;/*返回线性地址*/

  176. fail:
  177.         vfree(area->addr);
  178.         return NULL;
  179. }
复制代码
其中map_vm_area()建立页表映射机制的实现就是依次对pgd、pud、pmd、pte的设置。

二、非连续内存区的释放

调用vfree()函数实现
  1. view plaincopy to clipboardprint?/**
  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. }  
  19. /**
  20. *        vfree  -  release memory allocated by vmalloc()
  21. *        @addr:                memory base address
  22. *
  23. *        Free the virtually continuous memory area starting at @addr, as
  24. *        obtained from vmalloc(), vmalloc_32() or __vmalloc(). If @addr is
  25. *        NULL, no operation is performed.
  26. *
  27. *        Must not be called in interrupt context.
  28. */
  29. void vfree(const void *addr)
  30. {
  31.         BUG_ON(in_interrupt());
  32.         /*调试用*/
  33.         kmemleak_free(addr);
  34.         /*释放工作*/
  35.         __vunmap(addr, 1);
  36. }view plaincopy to clipboardprint?static void __vunmap(const void *addr, int deallocate_pages)  
  37. {  
  38.     struct vm_struct *area;  
  39.   
  40.     if (!addr)  
  41.         return;  
  42.   
  43.     if ((PAGE_SIZE-1) & (unsigned long)addr) {  
  44.         WARN(1, KERN_ERR "Trying to vfree() bad address (%p)\n", addr);  
  45.         return;  
  46.     }  
  47.     /*从vlist链表和红黑树中移除指定地址的线性区间*/  
  48.     area = remove_vm_area(addr);  
  49.     if (unlikely(!area)) {  
  50.         WARN(1, KERN_ERR "Trying to vfree() nonexistent vm area (%p)\n",  
  51.                 addr);  
  52.         return;  
  53.     }  
  54.   
  55.     debug_check_no_locks_freed(addr, area->size);  
  56.     debug_check_no_obj_freed(addr, area->size);  
  57.   
  58.     if (deallocate_pages) {  
  59.         int i;  
  60.   
  61.         for (i = 0; i < area->nr_pages; i++) {/*每次释放一个页面*/  
  62.             struct page *page = area->pages[i];  
  63.   
  64.             BUG_ON(!page);  
  65.             __free_page(page);  
  66.         }  
  67.   
  68.         if (area->flags & VM_VPAGES)/*在创建非连续区间时,如果页面
  69.             指针所占的空间大于一个页面时,从非连续内存区间
  70.             中分配。所以这里也就从相应的释放*/  
  71.             vfree(area->pages);  
  72.         else  
  73.             kfree(area->pages);/*从slab中释放*/  
  74.     }  
  75.   
  76.     kfree(area);/*释放area*/  
  77.     return;  
  78. }  
  79. static void __vunmap(const void *addr, int deallocate_pages)
  80. {
  81.         struct vm_struct *area;

  82.         if (!addr)
  83.                 return;

  84.         if ((PAGE_SIZE-1) & (unsigned long)addr) {
  85.                 WARN(1, KERN_ERR "Trying to vfree() bad address (%p)\n", addr);
  86.                 return;
  87.         }
  88.         /*从vlist链表和红黑树中移除指定地址的线性区间*/
  89.         area = remove_vm_area(addr);
  90.         if (unlikely(!area)) {
  91.                 WARN(1, KERN_ERR "Trying to vfree() nonexistent vm area (%p)\n",
  92.                                 addr);
  93.                 return;
  94.         }

  95.         debug_check_no_locks_freed(addr, area->size);
  96.         debug_check_no_obj_freed(addr, area->size);

  97.         if (deallocate_pages) {
  98.                 int i;

  99.                 for (i = 0; i < area->nr_pages; i++) {/*每次释放一个页面*/
  100.                         struct page *page = area->pages[i];

  101.                         BUG_ON(!page);
  102.                         __free_page(page);
  103.                 }

  104.                 if (area->flags & VM_VPAGES)/*在创建非连续区间时,如果页面
  105.                         指针所占的空间大于一个页面时,从非连续内存区间
  106.                         中分配。所以这里也就从相应的释放*/
  107.                         vfree(area->pages);
  108.                 else
  109.                         kfree(area->pages);/*从slab中释放*/
  110.         }

  111.         kfree(area);/*释放area*/
  112.         return;
  113. }view plaincopy to clipboardprint?/**
  114. *  remove_vm_area  -  find and remove a continuous kernel virtual area
  115. *  @addr:      base address
  116. *
  117. *  Search for the kernel VM area starting at @addr, and remove it.
  118. *  This function returns the found VM area, but using it is NOT safe
  119. *  on SMP machines, except for its size or flags.
  120. */  
  121. struct vm_struct *remove_vm_area(const void *addr)  
  122. {  
  123.     struct vmap_area *va;  
  124.     /*从红黑树种查找而不是链表,为了效率起见*/  
  125.     va = find_vmap_area((unsigned long)addr);  
  126.     if (va && va->flags & VM_VM_AREA) {  
  127.         struct vm_struct *vm = va->private;  
  128.         struct vm_struct *tmp, **p;  
  129.         /*
  130.          * remove from list and disallow access to this vm_struct
  131.          * before unmap. (address range confliction is maintained by
  132.          * vmap.)
  133.          */  
  134.         write_lock(&vmlist_lock);  
  135.         /*从链表中找到,然后删除*/  
  136.         for (p = &vmlist; (tmp = *p) != vm; p = &tmp->next)  
  137.             ;  
  138.         *p = tmp->next;  
  139.         write_unlock(&vmlist_lock);  
  140.         /*调试用*/  
  141.         vmap_debug_free_range(va->va_start, va->va_end);  
  142.         /*从红黑树中删除*/  
  143.         free_unmap_vmap_area(va);  
  144.         vm->size -= PAGE_SIZE;  
  145.   
  146.         return vm;  
  147.     }  
  148.     return NULL;  
  149. }  
  150. /**
  151. *        remove_vm_area  -  find and remove a continuous kernel virtual area
  152. *        @addr:                base address
  153. *
  154. *        Search for the kernel VM area starting at @addr, and remove it.
  155. *        This function returns the found VM area, but using it is NOT safe
  156. *        on SMP machines, except for its size or flags.
  157. */
  158. struct vm_struct *remove_vm_area(const void *addr)
  159. {
  160.         struct vmap_area *va;
  161.         /*从红黑树种查找而不是链表,为了效率起见*/
  162.         va = find_vmap_area((unsigned long)addr);
  163.         if (va && va->flags & VM_VM_AREA) {
  164.                 struct vm_struct *vm = va->private;
  165.                 struct vm_struct *tmp, **p;
  166.                 /*
  167.                  * remove from list and disallow access to this vm_struct
  168.                  * before unmap. (address range confliction is maintained by
  169.                  * vmap.)
  170.                  */
  171.                 write_lock(&vmlist_lock);
  172.                 /*从链表中找到,然后删除*/
  173.                 for (p = &vmlist; (tmp = *p) != vm; p = &tmp->next)
  174.                         ;
  175.                 *p = tmp->next;
  176.                 write_unlock(&vmlist_lock);
  177.                 /*调试用*/
  178.                 vmap_debug_free_range(va->va_start, va->va_end);
  179.                 /*从红黑树中删除*/
  180.                 free_unmap_vmap_area(va);
  181.                 vm->size -= PAGE_SIZE;

  182.                 return vm;
  183.         }
  184.         return NULL;
  185. }
复制代码
总结:linux高端内存非连续区的整体描述以及其分配和释放基本就总结完了。总结的只是一个大概的原理框架,不过根据这个框架对细节的了解应该不难。另外,其中涉及到伙伴系统、slab机制等部分需要再做分析和总结

论坛徽章:
0
2 [报告]
发表于 2012-01-06 16:29 |只看该作者
谢谢分享
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP