感谢支持
我们一直在努力

Linux高端内存管理之非连续内存区(描述)

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


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


相关阅读:


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


一、非连续内存区布局


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


[cpp]


  1. #define VMALLOC_START   ((unsigned long)high_memory + VMALLOC_OFFSET)  

[cpp]


  1. #define VMALLOC_OFFSET  (8 * 1024 * 1024)  

对于变量high_memory变量:


[cpp]


  1. 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. }   


其中,变量max_low_pfnhighmem_pfn_init()函数中初始化为下面值

[cpp]


  1. #define MAXMEM  (VMALLOC_END – PAGE_OFFSET – __VMALLOC_RESERVE)  

[cpp]


  1. <p>unsigned int __VMALLOC_RESERVE = 128 << 20;</p>  

对于非连续区间的结束定义:

[cpp]


  1. # define VMALLOC_END    (PKMAP_BASE – 2 * PAGE_SIZE)  


由上面的内核代码,画出内存布局细节图如下

Linux高端内存管理之非连续内存区(描述)

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


二、数据结构描述


虚拟内存区描述(对于vmlist链表)


[cpp]


  1. 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. };  


虚拟内存区描述(对于红黑树)

[html]


  1. 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. };  

内存区由next字段链接到一起,并且为了查找简单,他们以地址为次序。为了防止溢出,每个区域至少由一个页面隔离开。

Linux高端内存管理之非连续内存区(描述)
三、非连续内存区初始化
非连续内存区的初始化工作在start_kernel()->mm_init()->vmalloc_init()完成

[cpp]


  1. void __init vmalloc_init(void)  

  2. {  

  3.     struct vmap_area *va;  

  4.     struct vm_struct *tmp;  

  5.     int i;  

  6.   

  7.     for_each_possible_cpu(i) {  

  8.         struct vmap_block_queue *vbq;  

  9.   

  10.         vbq = &per_cpu(vmap_block_queue, i);  

  11.         spin_lock_init(&vbq->lock);  

  12.         INIT_LIST_HEAD(&vbq->free);  

  13.         INIT_LIST_HEAD(&vbq->dirty);  

  14.         vbq->nr_dirty = 0;  

  15.     }  

  16.   

  17.     /* Import existing vmlist entries. */  

  18.     for (tmp = vmlist; tmp; tmp = tmp->next) {/*导入vmlist中已经有的数据到红黑树中*/  

  19.         va = kzalloc(sizeof(struct vmap_area), GFP_NOWAIT);  

  20.         va->flags = tmp->flags | VM_VM_AREA;  

  21.         va->va_start = (unsigned long)tmp->addr;  

  22.         va->va_end = va->va_start + tmp->size;  

  23.         __insert_vmap_area(va);  

  24.     }  

  25.   

  26.     vmap_area_pcpu_hole = VMALLOC_END;  

  27.   

  28.     vmap_initialized = true;/*已经初始化*/  

  29. }  

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


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


get_vm_area()函数会调用__get_vm_area_node()函数

[cpp]


  1. 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. }  

[cpp]


  1. /* 

  2.  * Allocate a region of KVA of the specified size and alignment, within the 

  3.  * vstart and vend. 

  4.  */  

  5. static struct vmap_area *alloc_vmap_area(unsigned long size,  

  6.                 unsigned long align,  

  7.                 unsigned long vstart, unsigned long vend,  

  8.                 int node, gfp_t gfp_mask)  

  9. {  

  10.     struct vmap_area *va;  

  11.     struct rb_node *n;  

  12.     unsigned long addr;  

  13.     int purged = 0;  

  14.   

  15.     BUG_ON(!size);  

  16.     BUG_ON(size & ~PAGE_MASK);  

  17.     /*分配vmap_area结构*/  

  18.     va = kmalloc_node(sizeof(struct vmap_area),  

  19.             gfp_mask & GFP_RECLAIM_MASK, node);  

  20.     if (unlikely(!va))  

  21.         return ERR_PTR(-ENOMEM);  

  22.   

  23. retry:  

  24.     addr = ALIGN(vstart, align);  

  25.   

  26.     spin_lock(&vmap_area_lock);  

  27.     if (addr + size – 1 < addr)  

  28.         goto overflow;  

  29.   

  30.     /* XXX: could have a last_hole cache */  

  31.     n = vmap_area_root.rb_node;  

  32.     if (n) {  

  33.         struct vmap_area *first = NULL;  

  34.   

  35.         do {  

  36.             struct vmap_area *tmp;  

  37.             tmp = rb_entry(n, struct vmap_area, rb_node);  

  38.             if (tmp->va_end >= addr) {  

  39.                 if (!first && tmp->va_start < addr + size)  

  40.                     first = tmp;  

  41.                 n = n->rb_left;  

  42.             } else {  

  43.                 first = tmp;  

  44.                 n = n->rb_right;  

  45.             }  

  46.         } while (n);  

  47.   

  48.         if (!first)/*为最左的孩子,也就是比现有的都小*/  

  49.             goto found;  

  50.   

  51.         if (first->va_end < addr) {  

  52.             n = rb_next(&first->rb_node);  

  53.             if (n)  

  54.                 first = rb_entry(n, struct vmap_area, rb_node);  

  55.             else/*next为空*/  

  56.                 goto found;/*为找到的节点的下一个,也就是比找到的大*/  

  57.         }  

  58.         /*当上面没有满足要求时,重新配置addr,也就是起始 

  59.         地址*/  

  60.         while (addr + size > first->va_start && addr + size <= vend) {  

  61.             addr = ALIGN(first->va_end + PAGE_SIZE, align);/*重新配置起始地址*/  

  62.             if (addr + size – 1 < addr)  

  63.                 goto overflow;  

  64.   

  65.             n = rb_next(&first->rb_node);  

  66.             if (n)  

  67.                 first = rb_entry(n, struct vmap_area, rb_node);  

  68.             else  

  69.                 goto found;/*此时应该插入到找到的节点的右边*/  

  70.         }  

  71.     }  

  72. found:  

  73.     if (addr + size > vend) {  

  74. overflow:  

  75.         spin_unlock(&vmap_area_lock);  

  76.         if (!purged) {  

  77.             purge_vmap_area_lazy();  

  78.             purged = 1;  

  79.             goto retry;  

  80.         }  

  81.         if (printk_ratelimit())  

  82.             printk(KERN_WARNING  

  83.                 “vmap allocation for size %lu failed: “  

  84.                 “use vmalloc=<size> to increase size.\n”, size);  

  85.         kfree(va);  

  86.         return ERR_PTR(-EBUSY);  

  87.     }  

  88.   

  89.     BUG_ON(addr & (align-1));  

  90.     /*初始化va*/  

  91.     va->va_start = addr;  

  92.     va->va_end = addr + size;  

  93.     va->flags = 0;  

  94.     /*插入到红黑树*/  

  95.     __insert_vmap_area(va);  

  96.     spin_unlock(&vmap_area_lock);  

  97.   

  98.     return va;  

  99. }  

[cpp]


  1. static void insert_vmalloc_vm(struct vm_struct *vm, struct vmap_area *va,  

  2.                   unsigned long flags, void *caller)  

  3. {  

  4.     struct vm_struct *tmp, **p;  

  5.     /*初始化vm*/  

  6.     vm->flags = flags;  

  7.     vm->addr = (void *)va->va_start;  

  8.     vm->size = va->va_end – va->va_start;  

  9.     vm->caller = caller;  

  10.     va->private = vm;  

  11.     va->flags |= VM_VM_AREA;  

  12.   

  13.     write_lock(&vmlist_lock);  

  14.     /*寻找插入位置*/  

  15.     for (p = &vmlist; (tmp = *p) != NULL; p = &tmp->next) {  

  16.         if (tmp->addr >= vm->addr)  

  17.             break;  

  18.     }  

  19.     /*插入工作*/  

  20.     vm->next = *p;  

  21.     *p = vm;  

  22.     write_unlock(&vmlist_lock);  

  23. }  

初步总结了高端内存非连续区的管理框架,后面将总结他的分配和释放工作。

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

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

支付宝扫一扫打赏

微信扫一扫打赏