linux非连续内存,(转)linux高端内存管理之非连续内存区(描述)

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

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

一、非连续内存区布局

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

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

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

对于变量high_memory变量:

1.void__init initmem_init(unsignedlongstart_pfn,

2.unsignedlongend_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_pfn在highmem_pfn_init()函数中初始化为下面值

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

1.unsignedint__VMALLOC_RESERVE = 128 <

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

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链表)

1.structvm_struct {

2.structvm_struct    *next;

3.void*addr;/*内存区的第一个内存单元的线性地址*/

4.unsignedlongsize;

5.unsignedlongflags;/*类型*/

6.structpage     **pages;/*指向nr_pages数组的指针,该数组由指向页描述符的指针组成*/

7.unsignedintnr_pages;/*内存区填充的页的个数*/

8.unsignedlongphys_addr;/*该字段设为0,除非内存已被创建来映射一个硬件设备的IO共享内存*/

9.void*caller;

10.};

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

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()完成

1.void__init vmalloc_init(void)

2.{

3.structvmap_area *va;

4.structvm_struct *tmp;

5.inti;

6.

7.for_each_possible_cpu(i) {

8.structvmap_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(structvmap_area), GFP_NOWAIT);

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

21.va->va_start = (unsignedlong)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()函数

1.staticstructvm_struct *__get_vm_area_node(unsignedlongsize,

2.unsignedlongalign, unsignedlongflags, unsignedlongstart,

3.unsignedlongend,intnode, gfp_t gfp_mask,void*caller)

4.{

5.staticstructvmap_area *va;

6.structvm_struct *area;

7.

8.BUG_ON(in_interrupt());

9.if(flags & VM_IOREMAP) {

10.intbit = fls(size);

11.

12.if(bit > IOREMAP_MAX_ORDER)

13.bit = IOREMAP_MAX_ORDER;

14.elseif(bit

15.bit = PAGE_SHIFT;

16.

17.align = 1ul <

18.}

19.

20.size = PAGE_ALIGN(size);

21.if(unlikely(!size))

22.returnNULL;

23./*分配vm_struct结构体内存空间*/

24.area = kzalloc_node(sizeof(*area), gfp_mask & GFP_RECLAIM_MASK, node);

25.if(unlikely(!area))

26.returnNULL;

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.returnNULL;

37.}

38./*插入vmlist链表*/

39.insert_vmalloc_vm(area, va, flags, caller);

40.returnarea;

41.}

1./*

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

3.* vstart and vend.

4.*/

5.staticstructvmap_area *alloc_vmap_area(unsignedlongsize,

6.unsignedlongalign,

7.unsignedlongvstart, unsignedlongvend,

8.intnode, gfp_t gfp_mask)

9.{

10.structvmap_area *va;

11.structrb_node *n;

12.unsignedlongaddr;

13.intpurged = 0;

14.

15.BUG_ON(!size);

16.BUG_ON(size & ~PAGE_MASK);

17./*分配vmap_area结构*/

18.va = kmalloc_node(sizeof(structvmap_area),

19.gfp_mask & GFP_RECLAIM_MASK, node);

20.if(unlikely(!va))

21.returnERR_PTR(-ENOMEM);

22.

23.retry:

24.addr = ALIGN(vstart, align);

25.

26.spin_lock(&vmap_area_lock);

27.if(addr + size - 1

28.gotooverflow;

29.

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

31.n = vmap_area_root.rb_node;

32.if(n) {

33.structvmap_area *first = NULL;

34.

35.do{

36.structvmap_area *tmp;

37.tmp = rb_entry(n,structvmap_area, rb_node);

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

39.if(!first && tmp->va_start

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.gotofound;

50.

51.if(first->va_end

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

53.if(n)

54.first = rb_entry(n,structvmap_area, rb_node);

55.else/*next为空*/

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

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

63.gotooverflow;

64.

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

66.if(n)

67.first = rb_entry(n,structvmap_area, rb_node);

68.else

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

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.gotoretry;

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.returnERR_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.returnva;

99.}

1.staticvoidinsert_vmalloc_vm(structvm_struct *vm,structvmap_area *va,

2.unsignedlongflags,void*caller)

3.{

4.structvm_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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值