免费注册 查看新帖 |

Chinaunix

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

linux高端内存管理之非连续内存区(描述) [复制链接]

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








总结了高端内存区的固定内核映射区、临时内核映射与永久内核映射。但是对于高端内存中各个区间的布置我们任然不是很清楚,首先我们从整体上看看内核对高端内存的划分情况。

如果内存足够大(比如用户:内核线性空间=3:1,内核就只能访问线性空间的第4GB内容,如果物理内存超过1GB则视为足够大),内核线性空间无法同时映射所有内存。这就需要将内核线性空间分出一段不直接映射物理内存,而是作为窗口分时映射使用到的未映射的内存。

一、非连续内存区布局

Linux内核中对于非连续区间的开始:


view plaincopy to clipboardprint?#define VMALLOC_START   ((unsigned long)high_memory + VMALLOC_OFFSET)  
#define VMALLOC_START        ((unsigned long)high_memory + VMALLOC_OFFSET)view plaincopy to clipboardprint?#define VMALLOC_OFFSET  (8 * 1024 * 1024)  
#define VMALLOC_OFFSET        (8 * 1024 * 1024)对于变量high_memory变量:
  1. view plaincopy to clipboardprint?void __init initmem_init(unsigned long start_pfn,  
  2.                   unsigned long end_pfn)  
  3. {  
  4.     highstart_pfn = highend_pfn = max_pfn;  
  5.     if (max_pfn > max_low_pfn)  
  6.         highstart_pfn = max_low_pfn;  
  7. ……  
  8.     num_physpages = highend_pfn;  
  9.     /*高端内存开始地址物理*/  
  10.     high_memory = (void *) __va(highstart_pfn * PAGE_SIZE - 1) + 1;  
  11. ……  
  12. }   
  13. void __init initmem_init(unsigned long start_pfn,
  14.                                   unsigned long end_pfn)
  15. {
  16.         highstart_pfn = highend_pfn = max_pfn;
  17.         if (max_pfn > max_low_pfn)
  18.                 highstart_pfn = max_low_pfn;
  19. ……
  20.         num_physpages = highend_pfn;
  21.         /*高端内存开始地址物理*/
  22.         high_memory = (void *) __va(highstart_pfn * PAGE_SIZE - 1) + 1;
  23. ……
  24. }  
复制代码
其中,变量max_low_pfn在highmem_pfn_init()函数中初始化为下面值
  1. view plaincopy to clipboardprint?#define MAXMEM  (VMALLOC_END - PAGE_OFFSET - __VMALLOC_RESERVE)  
  2. #define MAXMEM        (VMALLOC_END - PAGE_OFFSET - __VMALLOC_RESERVE)view plaincopy to clipboardprint?<p>unsigned int __VMALLOC_RESERVE = 128 << 20;</p>  
  3. <p>unsigned int __VMALLOC_RESERVE = 128 << 20;</p>对于非连续区间的结束定义:

  4. view plaincopy to clipboardprint?# define VMALLOC_END    (PKMAP_BASE - 2 * PAGE_SIZE)  
  5. # define VMALLOC_END        (PKMAP_BASE - 2 * PAGE_SIZE)
复制代码
由上面的内核代码,画出内存布局细节图如下



由上面的布局可知128M+4M+4M+8K,然而直接映射区和连续内存之间空出来了8M的空间不能用,非连续空间和永久内核映射区之间也有8K的空间不可用,另外,内存顶端空出了4K不可用的。这样,高端内存能用的空间为128M+4M+4M+8K-4K-8M-8K=128M-4K大小的内存。

二、数据结构描述

虚拟内存区描述(对于vmlist链表)
  1. view plaincopy to clipboardprint?struct vm_struct {  
  2.     struct vm_struct    *next;  
  3.     void            *addr;/*内存区的第一个内存单元的线性地址*/  
  4.     unsigned long       size;  
  5.     unsigned long       flags;/*类型*/  
  6.     struct page     **pages;/*指向nr_pages数组的指针,该数组由指向页描述符的指针组成*/  
  7.     unsigned int        nr_pages;/*内存区填充的页的个数*/  
  8.     unsigned long       phys_addr;/*该字段设为0,除非内存已被创建来映射一个硬件设备的IO共享内存*/  
  9.     void            *caller;  
  10. };  
  11. struct vm_struct {
  12.         struct vm_struct        *next;
  13.         void                        *addr;/*内存区的第一个内存单元的线性地址*/
  14.         unsigned long                size;
  15.         unsigned long                flags;/*类型*/
  16.         struct page                **pages;/*指向nr_pages数组的指针,该数组由指向页描述符的指针组成*/
  17.         unsigned int                nr_pages;/*内存区填充的页的个数*/
  18.         unsigned long                phys_addr;/*该字段设为0,除非内存已被创建来映射一个硬件设备的IO共享内存*/
  19.         void                        *caller;
  20. };
复制代码
虚拟内存区描述(对于红黑树)
  1. view plaincopy to clipboardprint?struct vmap_area {  
  2.     unsigned long va_start;  
  3.     unsigned long va_end;  
  4.     unsigned long flags;  
  5.     struct rb_node rb_node;     /* address sorted rbtree */  
  6.     struct list_head list;      /* address sorted list */  
  7.     struct list_head purge_list;    /* "lazy purge" list */  
  8.     void *private;  
  9.     struct rcu_head rcu_head;  
  10. };  
  11. struct vmap_area {
  12.         unsigned long va_start;
  13.         unsigned long va_end;
  14.         unsigned long flags;
  15.         struct rb_node rb_node;                /* address sorted rbtree */
  16.         struct list_head list;                /* address sorted list */
  17.         struct list_head purge_list;        /* "lazy purge" list */
  18.         void *private;
  19.         struct rcu_head rcu_head;
  20. };
复制代码
内存区由next字段链接到一起,并且为了查找简单,他们以地址为次序。为了防止溢出,每个区域至少由一个页面隔离开。



三、创建非连续内存的线性区

vm_struct结构链接在一个链表中,链表的第一个元素的地址存放在vmlist变量中。当内核需要分配一块新的内存时,函数get_vm_area()分配结构体所需要的空间,然后将其插入到链表中。另外,该版本的内核中增加了红黑树的管理。函数get_vm_area()不仅要将其插入到vmlist链表中,还有将结构体vmap_area插入到vmap_area_root指定根的红黑树中。

get_vm_area()函数会调用__get_vm_area_node()函数
  1. view plaincopy to clipboardprint?static struct vm_struct *__get_vm_area_node(unsigned long size,  
  2.         unsigned long align, unsigned long flags, unsigned long start,  
  3.         unsigned long end, int node, gfp_t gfp_mask, void *caller)  
  4. {  
  5.     static struct vmap_area *va;  
  6.     struct vm_struct *area;  
  7.   
  8.     BUG_ON(in_interrupt());  
  9.     if (flags & VM_IOREMAP) {  
  10.         int bit = fls(size);  
  11.   
  12.         if (bit > IOREMAP_MAX_ORDER)  
  13.             bit = IOREMAP_MAX_ORDER;  
  14.         else if (bit < PAGE_SHIFT)  
  15.             bit = PAGE_SHIFT;  
  16.   
  17.         align = 1ul << bit;  
  18.     }  
  19.   
  20.     size = PAGE_ALIGN(size);  
  21.     if (unlikely(!size))  
  22.         return NULL;  
  23.     /*分配vm_struct结构体内存空间*/  
  24.     area = kzalloc_node(sizeof(*area), gfp_mask & GFP_RECLAIM_MASK, node);  
  25.     if (unlikely(!area))  
  26.         return NULL;  
  27.   
  28.     /*
  29.      * We always allocate a guard page.
  30.      */  
  31.     size += PAGE_SIZE;/*为安全考虑,多一个页面*/  
  32.     /*分配vmap_area结构体,并且将其插入到红黑树中*/  
  33.     va = alloc_vmap_area(size, align, start, end, node, gfp_mask);  
  34.     if (IS_ERR(va)) {  
  35.         kfree(area);  
  36.         return NULL;  
  37.     }  
  38.     /*插入vmlist链表*/  
  39.     insert_vmalloc_vm(area, va, flags, caller);  
  40.     return area;  
  41. }  
  42. static struct vm_struct *__get_vm_area_node(unsigned long size,
  43.                 unsigned long align, unsigned long flags, unsigned long start,
  44.                 unsigned long end, int node, gfp_t gfp_mask, void *caller)
  45. {
  46.         static struct vmap_area *va;
  47.         struct vm_struct *area;

  48.         BUG_ON(in_interrupt());
  49.         if (flags & VM_IOREMAP) {
  50.                 int bit = fls(size);

  51.                 if (bit > IOREMAP_MAX_ORDER)
  52.                         bit = IOREMAP_MAX_ORDER;
  53.                 else if (bit < PAGE_SHIFT)
  54.                         bit = PAGE_SHIFT;

  55.                 align = 1ul << bit;
  56.         }

  57.         size = PAGE_ALIGN(size);
  58.         if (unlikely(!size))
  59.                 return NULL;
  60.         /*分配vm_struct结构体内存空间*/
  61.         area = kzalloc_node(sizeof(*area), gfp_mask & GFP_RECLAIM_MASK, node);
  62.         if (unlikely(!area))
  63.                 return NULL;

  64.         /*
  65.          * We always allocate a guard page.
  66.          */
  67.         size += PAGE_SIZE;/*为安全考虑,多一个页面*/
  68.         /*分配vmap_area结构体,并且将其插入到红黑树中*/
  69.         va = alloc_vmap_area(size, align, start, end, node, gfp_mask);
  70.         if (IS_ERR(va)) {
  71.                 kfree(area);
  72.                 return NULL;
  73.         }
  74.         /*插入vmlist链表*/
  75.         insert_vmalloc_vm(area, va, flags, caller);
  76.         return area;
  77. }view plaincopy to clipboardprint?/*
  78. * Allocate a region of KVA of the specified size and alignment, within the
  79. * vstart and vend.
  80. */  
  81. static struct vmap_area *alloc_vmap_area(unsigned long size,  
  82.                 unsigned long align,  
  83.                 unsigned long vstart, unsigned long vend,  
  84.                 int node, gfp_t gfp_mask)  
  85. {  
  86.     struct vmap_area *va;  
  87.     struct rb_node *n;  
  88.     unsigned long addr;  
  89.     int purged = 0;  
  90.   
  91.     BUG_ON(!size);  
  92.     BUG_ON(size & ~PAGE_MASK);  
  93.     /*分配vmap_area结构*/  
  94.     va = kmalloc_node(sizeof(struct vmap_area),  
  95.             gfp_mask & GFP_RECLAIM_MASK, node);  
  96.     if (unlikely(!va))  
  97.         return ERR_PTR(-ENOMEM);  
  98.   
  99. retry:  
  100.     addr = ALIGN(vstart, align);  
  101.   
  102.     spin_lock(&vmap_area_lock);  
  103.     if (addr + size - 1 < addr)  
  104.         goto overflow;  
  105.   
  106.     /* XXX: could have a last_hole cache */  
  107.     n = vmap_area_root.rb_node;  
  108.     if (n) {  
  109.         struct vmap_area *first = NULL;  
  110.   
  111.         do {  
  112.             struct vmap_area *tmp;  
  113.             tmp = rb_entry(n, struct vmap_area, rb_node);  
  114.             if (tmp->va_end >= addr) {  
  115.                 if (!first && tmp->va_start < addr + size)  
  116.                     first = tmp;  
  117.                 n = n->rb_left;  
  118.             } else {  
  119.                 first = tmp;  
  120.                 n = n->rb_right;  
  121.             }  
  122.         } while (n);  
  123.   
  124.         if (!first)/*为最左的孩子,也就是比现有的都小*/  
  125.             goto found;  
  126.   
  127.         if (first->va_end < addr) {  
  128.             n = rb_next(&first->rb_node);  
  129.             if (n)  
  130.                 first = rb_entry(n, struct vmap_area, rb_node);  
  131.             else/*next为空*/  
  132.                 goto found;/*为找到的节点的下一个,也就是比找到的大*/  
  133.         }  
  134.         /*当上面没有满足要求时,重新配置addr,也就是起始
  135.         地址*/  
  136.         while (addr + size > first->va_start && addr + size <= vend) {  
  137.             addr = ALIGN(first->va_end + PAGE_SIZE, align);/*重新配置起始地址*/  
  138.             if (addr + size - 1 < addr)  
  139.                 goto overflow;  
  140.   
  141.             n = rb_next(&first->rb_node);  
  142.             if (n)  
  143.                 first = rb_entry(n, struct vmap_area, rb_node);  
  144.             else  
  145.                 goto found;/*此时应该插入到找到的节点的右边*/  
  146.         }  
  147.     }  
  148. found:  
  149.     if (addr + size > vend) {  
  150. overflow:  
  151.         spin_unlock(&vmap_area_lock);  
  152.         if (!purged) {  
  153.             purge_vmap_area_lazy();  
  154.             purged = 1;  
  155.             goto retry;  
  156.         }  
  157.         if (printk_ratelimit())  
  158.             printk(KERN_WARNING  
  159.                 "vmap allocation for size %lu failed: "  
  160.                 "use vmalloc=<size> to increase size.\n", size);  
  161.         kfree(va);  
  162.         return ERR_PTR(-EBUSY);  
  163.     }  
  164.   
  165.     BUG_ON(addr & (align-1));  
  166.     /*初始化va*/  
  167.     va->va_start = addr;  
  168.     va->va_end = addr + size;  
  169.     va->flags = 0;  
  170.     /*插入到红黑树*/  
  171.     __insert_vmap_area(va);  
  172.     spin_unlock(&vmap_area_lock);  
  173.   
  174.     return va;  
  175. }  
  176. /*
  177. * Allocate a region of KVA of the specified size and alignment, within the
  178. * vstart and vend.
  179. */
  180. static struct vmap_area *alloc_vmap_area(unsigned long size,
  181.                                 unsigned long align,
  182.                                 unsigned long vstart, unsigned long vend,
  183.                                 int node, gfp_t gfp_mask)
  184. {
  185.         struct vmap_area *va;
  186.         struct rb_node *n;
  187.         unsigned long addr;
  188.         int purged = 0;

  189.         BUG_ON(!size);
  190.         BUG_ON(size & ~PAGE_MASK);
  191.         /*分配vmap_area结构*/
  192.         va = kmalloc_node(sizeof(struct vmap_area),
  193.                         gfp_mask & GFP_RECLAIM_MASK, node);
  194.         if (unlikely(!va))
  195.                 return ERR_PTR(-ENOMEM);

  196. retry:
  197.         addr = ALIGN(vstart, align);

  198.         spin_lock(&vmap_area_lock);
  199.         if (addr + size - 1 < addr)
  200.                 goto overflow;

  201.         /* XXX: could have a last_hole cache */
  202.         n = vmap_area_root.rb_node;
  203.         if (n) {
  204.                 struct vmap_area *first = NULL;

  205.                 do {
  206.                         struct vmap_area *tmp;
  207.                         tmp = rb_entry(n, struct vmap_area, rb_node);
  208.                         if (tmp->va_end >= addr) {
  209.                                 if (!first && tmp->va_start < addr + size)
  210.                                         first = tmp;
  211.                                 n = n->rb_left;
  212.                         } else {
  213.                                 first = tmp;
  214.                                 n = n->rb_right;
  215.                         }
  216.                 } while (n);

  217.                 if (!first)/*为最左的孩子,也就是比现有的都小*/
  218.                         goto found;

  219.                 if (first->va_end < addr) {
  220.                         n = rb_next(&first->rb_node);
  221.                         if (n)
  222.                                 first = rb_entry(n, struct vmap_area, rb_node);
  223.                         else/*next为空*/
  224.                                 goto found;/*为找到的节点的下一个,也就是比找到的大*/
  225.                 }
  226.                 /*当上面没有满足要求时,重新配置addr,也就是起始
  227.                 地址*/
  228.                 while (addr + size > first->va_start && addr + size <= vend) {
  229.                         addr = ALIGN(first->va_end + PAGE_SIZE, align);/*重新配置起始地址*/
  230.                         if (addr + size - 1 < addr)
  231.                                 goto overflow;

  232.                         n = rb_next(&first->rb_node);
  233.                         if (n)
  234.                                 first = rb_entry(n, struct vmap_area, rb_node);
  235.                         else
  236.                                 goto found;/*此时应该插入到找到的节点的右边*/
  237.                 }
  238.         }
  239. found:
  240.         if (addr + size > vend) {
  241. overflow:
  242.                 spin_unlock(&vmap_area_lock);
  243.                 if (!purged) {
  244.                         purge_vmap_area_lazy();
  245.                         purged = 1;
  246.                         goto retry;
  247.                 }
  248.                 if (printk_ratelimit())
  249.                         printk(KERN_WARNING
  250.                                 "vmap allocation for size %lu failed: "
  251.                                 "use vmalloc=<size> to increase size.\n", size);
  252.                 kfree(va);
  253.                 return ERR_PTR(-EBUSY);
  254.         }

  255.         BUG_ON(addr & (align-1));
  256.         /*初始化va*/
  257.         va->va_start = addr;
  258.         va->va_end = addr + size;
  259.         va->flags = 0;
  260.         /*插入到红黑树*/
  261.         __insert_vmap_area(va);
  262.         spin_unlock(&vmap_area_lock);

  263.         return va;
  264. }view plaincopy to clipboardprint?static void insert_vmalloc_vm(struct vm_struct *vm, struct vmap_area *va,  
  265.                   unsigned long flags, void *caller)  
  266. {  
  267.     struct vm_struct *tmp, **p;  
  268.     /*初始化vm*/  
  269.     vm->flags = flags;  
  270.     vm->addr = (void *)va->va_start;  
  271.     vm->size = va->va_end - va->va_start;  
  272.     vm->caller = caller;  
  273.     va->private = vm;  
  274.     va->flags |= VM_VM_AREA;  
  275.   
  276.     write_lock(&vmlist_lock);  
  277.     /*寻找插入位置*/  
  278.     for (p = &vmlist; (tmp = *p) != NULL; p = &tmp->next) {  
  279.         if (tmp->addr >= vm->addr)  
  280.             break;  
  281.     }  
  282.     /*插入工作*/  
  283.     vm->next = *p;  
  284.     *p = vm;  
  285.     write_unlock(&vmlist_lock);  
  286. }  
  287. static void insert_vmalloc_vm(struct vm_struct *vm, struct vmap_area *va,
  288.                               unsigned long flags, void *caller)
  289. {
  290.         struct vm_struct *tmp, **p;
  291.         /*初始化vm*/
  292.         vm->flags = flags;
  293.         vm->addr = (void *)va->va_start;
  294.         vm->size = va->va_end - va->va_start;
  295.         vm->caller = caller;
  296.         va->private = vm;
  297.         va->flags |= VM_VM_AREA;

  298.         write_lock(&vmlist_lock);
  299.         /*寻找插入位置*/
  300.         for (p = &vmlist; (tmp = *p) != NULL; p = &tmp->next) {
  301.                 if (tmp->addr >= vm->addr)
  302.                         break;
  303.         }
  304.         /*插入工作*/
  305.         vm->next = *p;
  306.         *p = vm;
  307.         write_unlock(&vmlist_lock);
  308. }
复制代码
初步总结了高端内存非连续区的管理框架,后面将总结他的分配和释放工作。

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

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP