vmalloc申请线性地址空间(linux 2.4.22)

参考《ULK》 p343,vmalloc线性地址区范围为VMALLOC_START~ VMALLOC_END(ULK中定义为非连续内存区),在Physicalmemory mapping的末尾与第一个vmalloc area之间插入一个8M的隔离区,目的是为了捕获对内存的越界访问。出于同样的理由,插入4KB大小的安全区来隔离非连续的内存区。内存布局见图1。

vmalloc申请线性地址空间(linux 2.4.22)_第1张图片

图1 线性地址空间区间

用户通过vmalloc申请内存时,将获得非连续内存区VMALLOC_START ~ VMALLOC_END中一小段线性地址空间,对应上图中的vmallocarea,该区间用struct vm_struct结构体描述。

include/linux/vmalloc.h

struct vm_struct {

         unsigned long flags;

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

         unsigned long size;//内存区的大小加4KB安全区

         struct vm_struct * next;//指向下一个vm_struct结构的指针

};

内核把所有已经分配出去的线性地址空间(图1中阴影vmalloc area)用vmlist链表管理。当需要在非连续内存区中申请size大小的线性地址空间时,扫描vmlist链表(链表中的元素为已经分配出去的地址空间),找到空闲的地址空间。

mm/vmalloc.c

@size是要申请的线性地址空间大小

struct vm_struct * get_vm_area(unsigned long size, unsigned long flags)

{

         unsigned long addr, next;

         struct vm_struct **p, *tmp, *area;

 

         area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL);

         size += PAGE_SIZE;

         addr = VMALLOC_START;

         for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {

                   if (size + addr <= (unsigned long) tmp->addr)

                            break;

                   next = tmp->size + (unsigned long) tmp->addr;

                   if (next > addr)

                            addr = next;

                   if (addr > VMALLOC_END-size)

                            goto out;

         }

         //area插入vmlist链表,代码省略

out:

         return NULL;

}

 

下面结合具体例子分析代码:

假设非连续内存区当前情况如图2,阴影部分表示已经分配出去的线性地址空间(含实际申请的size大小+4KB安全区),空白部分表示空闲的地址空间。用户通过vmalloc申请的线性地址空间大于area 2的大小,area 4区间可以满足用户需求。

vmalloc申请线性地址空间(linux 2.4.22)_第2张图片

图2 非连续内存区实际使用情况

对应到代码中,已分配出去的area通过vm_struct结构体管理,存放在vmlist链表中,链表的第一个元素对应area1,链表元素通过vm_struct->next指针管理。

vmalloc申请线性地址空间(linux 2.4.22)_第3张图片

图3 vm_struct组成的vmallocarea链表

 

对照图3,用户申请size大小的线性地址空间,需要扫描vmlist链表,找到前一个阴影area的vm_struct结构,该vm_struct->addr作为起始地址,加上size长度,如果小于下一个阴影area的起始地址vm_struct->addr,则说明这两个阴影之间的空闲线性地址区间满足vmalloc要求。

对于图3中从VMALLOC_START起始地址处就已经分配了一段区间出去的情况,上面描述中“找到前一个阴影area的vm_struct结构,该vm_struct->addr作为起始地址”直接用VMALLOC_START作为起始地址值。

你可能感兴趣的:(内存管理)