vm

    xiaoxiao2021-03-25  210

    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

    最新回复(0)