vm_area_struct 用于表示0~3G的空间中一段连续的虚拟地址空间,是给user space的process使用.
vm_struct 是kernel space 除low memory中用于表示连续的虚拟地址空间,常用于vmalloc/vfree的操作
struct vm_struct {
struct vm_struct *next; //表示下一个vm,所有的vm组成一个链表
void *addr;//虚拟地址
unsigned long size; //大小
unsigned long flags;
struct page **pages;//vm所映射的page
unsigned int nr_pages;//vm 所映射的page 的个数
phys_addr_t phys_addr;//对应起始的物理地址和addr相对应
const void *caller;//当前调用vm的指针
};
vm_struct的常用操作如下:可以看出这些都返回vm_struct
extern struct vm_struct *get_vm_area(unsigned long size, unsigned long flags);
extern struct vm_struct *get_vm_area_caller(unsigned long size,
unsigned long flags, const void *caller);
extern struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags,
unsigned long start, unsigned long end);
extern struct vm_struct *__get_vm_area_caller(unsigned long size,
unsigned long flags,
unsigned long start, unsigned long end,
const void *caller);
extern struct vm_struct *remove_vm_area(const void *addr);
extern struct vm_struct *find_vm_area(const void *addr);
struct vm_struct *get_vm_area(unsigned long size, unsigned long flags)
{
return __get_vm_area_node(size, 1, flags, VMALLOC_START, VMALLOC_END,
NUMA_NO_NODE, GFP_KERNEL,
__builtin_return_address(0));
}
可见是从VMALLOC_START 和 VMALLOC_END 之间分配内存,也间接证实了vm_struct 是kernel space管理虚拟内存的。
static struct vm_struct *__get_vm_area_node(unsigned long size,
unsigned long align, unsigned long flags, unsigned long start,
unsigned long end, int node, gfp_t gfp_mask, const void *caller)
{
struct vmap_area *va;
struct vm_struct *area;
BUG_ON(in_interrupt());
size = PAGE_ALIGN(size);
if (unlikely(!size))
return NULL;
if (flags & VM_IOREMAP)
align = 1ul << clamp_t(int, get_count_order_long(size),
PAGE_SHIFT, IOREMAP_MAX_ORDER);
area = kzalloc_node(sizeof(*area), gfp_mask & GFP_RECLAIM_MASK, node);
if (unlikely(!area))
return NULL;
if (!(flags & VM_NO_GUARD))
size += PAGE_SIZE;
va = alloc_vmap_area(size, align, start, end, node, gfp_mask);
if (IS_ERR(va)) {
kfree(area);
return NULL;
}
setup_vmalloc_vm(area, va, flags, caller);
return area;
}
__get_vm_area_node 的实现也很简单
首先申请一个vm_struct area的结构
area = kzalloc_node(sizeof(*area), gfp_mask & GFP_RECLAIM_MASK, node);
然后通过alloc_vmap_area 申请已经映射过的虚拟内存,已经映射的虚拟内存用vmap_area 表示,这段虚拟内存用rb tree表示,是在开机的过程中
vmalloc_init->__insert_vmap_area来插入的
static void __insert_vmap_area(struct vmap_area *va)
{
struct rb_node **p = &vmap_area_root.rb_node;
struct rb_node *parent = NULL;
struct rb_node *tmp;
while (*p) {
struct vmap_area *tmp_va;
parent = *p;
tmp_va = rb_entry(parent, struct vmap_area, rb_node);
if (va->va_start < tmp_va->va_end)
p = &(*p)->rb_left;
else if (va->va_end > tmp_va->va_start)
p = &(*p)->rb_right;
else
BUG();
}
rb_link_node(&va->rb_node, parent, p);
rb_insert_color(&va->rb_node, &vmap_area_root);
/* address-sort this list */
tmp = rb_prev(&va->rb_node);
if (tmp) {
struct vmap_area *prev;
prev = rb_entry(tmp, struct vmap_area, rb_node);
list_add_rcu(&va->list, &prev->list);
} else
list_add_rcu(&va->list, &vmap_area_list);
}
注意这个rb tree的root是vmap_area_root.rb_node。
回到__get_vm_area_node 中通过alloc_vmap_area 申请到虚拟内存后,就调用setup_vmalloc_vm 填充vm_struct
static void setup_vmalloc_vm(struct vm_struct *vm, struct vmap_area *va,
unsigned long flags, const void *caller)
{
spin_lock(&vmap_area_lock);
vm->flags = flags;
vm->addr = (void *)va->va_start;
vm->size = va->va_end - va->va_start;
vm->caller = caller;
va->vm = vm;
va->flags |= VM_VM_AREA;
spin_unlock(&vmap_area_lock);
}
而vm_area_struct 是给进程用的,常用find_vma 来找到虚拟地址所在的vm_area_struct
struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr)
{
struct rb_node *rb_node;
struct vm_area_struct *vma;
/* Check the cache first. */
vma = vmacache_find(mm, addr);
if (likely(vma))
return vma;
rb_node = mm->mm_rb.rb_node;
while (rb_node) {
struct vm_area_struct *tmp;
tmp = rb_entry(rb_node, struct vm_area_struct, vm_rb);
if (tmp->vm_end > addr) {
vma = tmp;
if (tmp->vm_start <= addr)
break;
rb_node = rb_node->rb_left;
} else
rb_node = rb_node->rb_right;
}
if (vma)
vmacache_update(addr, vma);
return vma;
}
可见也是用rb tree表示,但是这个rb tree的根节点是在自身mm中保存,每个进程都有自己的mm。如果能找到就返回vm_area_struct。负责就发生page fault。
从find_vma 中可以看到查找的时候会首先从vm cache中也就是vmacache_find 中查找,最后将查找的结构更新到vmacache_update 中.
转载请注明原文地址: https://ju.6miu.com/read-2525.html