Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1071534
  • 博文数量: 277
  • 博客积分: 8313
  • 博客等级: 中将
  • 技术积分: 2976
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-22 11:25
文章分类

全部博文(277)

文章存档

2013年(17)

2012年(66)

2011年(104)

2010年(90)

我的朋友

分类: LINUX

2012-05-16 10:29:59

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

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

一、非连续内存区布局

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

[cpp] view plaincopyprint?

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

[cpp] view plaincopyprint?

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

对于变量high_memory变量:

[cpp] view plaincopyprint?

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] view plaincopyprint?

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

[cpp] view plaincopyprint?

1.      

unsigned int __VMALLOC_RESERVE = 128 << 20;

  

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

[cpp] view plaincopyprint?

1.       # 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链表)

[cpp] view plaincopyprint?

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] view plaincopyprint?

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字段链接到一起,并且为了查找简单,他们以地址为次序。为了防止溢出,每个区域至少由一个页面隔离开。

三、非连续内存区初始化

非连续内存区的初始化工作在start_kernel()->mm_init()->vmalloc_init()完成

[cpp] view plaincopyprint?

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] view plaincopyprint?

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] view plaincopyprint?

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= 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] view plaincopyprint?

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

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

 

阅读(1106) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~