/*
* Determine the object being mapped and call the appropriate * specific mapper. the address has already been validated, but * not unmapped, but the maps are removed from the list. */
vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL); if (!vma) { }
vma->vm_mm = mm; vma->vm_start = addr; vma->vm_end = addr + len; vma->vm_flags = vm_flags;
vma->vm_page_prot = vm_get_page_prot(vm_flags); vma->vm_pgoff = pgoff;
INIT_LIST_HEAD(&vma->anon_vma_chain); if (file) {
if (vm_flags& VM_DENYWRITE) { }
if (vm_flags& VM_SHARED) { }
/* ->mmap() can change vma->vm_file, but must guarantee that * vma_link() below can deny write-access if VM_DENYWRITE is set * and map writably if VM_SHARED is set. This usually means the * new file must not have been exposed to user-space, yet. */
vma->vm_file = get_file(file); error = file->f_op->mmap(file, vma); if (error)
goto unmap_and_free_vma;
error = mapping_map_writable(file->f_mapping); if (error)
goto allow_write_and_free_vma; error = deny_write_access(file); if (error)
goto free_vma;
error = -ENOMEM; goto unacct_error;
/* Can addr have changed?? *
* Answer: Yes, several device drivers can do it in their
}
* f_op->mmap method. -DaveM
* Bug: If addr is changed, prev, rb_link, rb_parent should * be updated for vma_link() */
WARN_ON_ONCE(addr != vma->vm_start); addr = vma->vm_start; vm_flags = vma->vm_flags; error = shmem_zero_setup(vma); if (error)
goto free_vma;
} elseif (vm_flags& VM_SHARED) {
vma_link(mm, vma, prev, rb_link, rb_parent);
/* Once vma denies write, undo our temporary denial count */ if (file) { }
file = vma->vm_file; perf_event_mmap(vma);
vm_stat_account(mm, vm_flags, len>> PAGE_SHIFT); if (vm_flags& VM_LOCKED) { } if (file) /*
* New (or expanded) vma always get soft dirty status. * Otherwise user-space soft-dirty page tracker won't * be able to distinguish situation when vma area unmapped, * then new mapped in-place (which must be aimed as * a completely new data area).
uprobe_mmap(vma);
if (!((vm_flags& VM_SPECIAL) || is_vm_hugetlb_page(vma) ||
vma == get_gate_vma(current->mm)))
mm->locked_vm += (len>> PAGE_SHIFT); vma->vm_flags &= VM_LOCKED_CLEAR_MASK; if (vm_flags& VM_SHARED)
mapping_unmap_writable(file->f_mapping); allow_write_access(file); if (vm_flags& VM_DENYWRITE)
out:
else
*/
vma->vm_flags |= VM_SOFTDIRTY; vma_set_page_prot(vma); returnaddr;
unmap_and_free_vma: }
/* Undo any partial mapping done by a device driver. */ unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end); charged = 0;
if (vm_flags& VM_SHARED)
mapping_unmap_writable(file->f_mapping); vma->vm_file = NULL; fput(file);
allow_write_and_free_vma:
if (vm_flags& VM_DENYWRITE)
allow_write_access(file);
free_vma:
kmem_cache_free(vm_area_cachep, vma); if (charged)
vm_unacct_memory(charged); return error; unacct_error:
算法伪代码: mmap_region() {
寻找前后一个区域的vm_area_struct实例以及红黑树中节点对应的数据; if(在指定的映射位置已经存在映射)
do_munmap(); 检查内存空间是否满足需求,是否需要扩充;
如果是匿名映射(file为空),并且这个虚拟区是非共享的,则可以把这个虚拟区和*与它紧挨的前一个虚拟区进行合并;虚拟区的合并是由vma_merge()函数实现的。如*果合并成功,则转out处,请看后面out处的代码;
创建新的vm_area_strcut实例; file->f_op->mmap();//创建映射 if(VM_LOCKED)
扫描映射中各页发出的中断以便读取数据; retuen 映射的起始地址; }
(5)linux使用do_munmap()函数取消断开可执行映像向虚存区域的映射,删除有关的虚
存区域,定义在/mm/mmap.c中;
int do_munmap(structmm_struct *mm, unsignedlongstart, size_t len) {
error = __split_vma(mm, vma, start, 0);
/*
* Make sure that map_count on return from munmap() will * not exceed its limit; but let map_count go just above * its limit temporarily, to help free resources as expected. */
if (end < vma->vm_end &&mm->map_count >= sysctl_max_map_count)
return -ENOMEM;
/*
* If we need to split any vma, do it now to save pain later. *
* Note: mremap's move_vma VM_ACCOUNT handling assumes a partially * unmapped vm_area_struct will remain in use: so lower split_vma * places tmp vma above, and higher split_vma places tmp vma below. */
if (start> vma->vm_start) {
int error;
/* if it doesn't overlap, we have nothing.. */ end = start + len; if (vma->vm_start >= end)
return 0;
/* Find the first overlapping VMA */ vma = find_vma(mm, start); if (!vma)
return 0; prev = vma->vm_prev;
/* we have start < vma->vm_end */ len = PAGE_ALIGN(len); if (len == 0)
return -EINVAL;
if ((offset_in_page(start)) || start> TASK_SIZE || len> TASK_SIZE-start)
return -EINVAL; unsignedlong end;
structvm_area_struct *vma, *prev, *last;
}
}
if (error)
return error; prev = vma;
/* Does it split the last one? */ last = find_vma(mm, end);
if (last && end > last->vm_start) { }
vma = prev ? prev->vm_next : mm->mmap; /*
* unlock any mlock()ed ranges before detaching vmas */
if (mm->locked_vm) { } /*
* Remove the vma's, and unmap the actual pages */
detach_vmas_to_be_unmapped(mm, vma, prev, end); unmap_region(mm, vma, prev, start, end); arch_unmap(mm, vma, start, end); /* Fix up all other VM information */ remove_vma_list(mm, vma); return 0;
structvm_area_struct *tmp = vma; while (tmp && tmp->vm_start < end) { }
if (tmp->vm_flags & VM_LOCKED) { }
tmp = tmp->vm_next;
mm->locked_vm -= vma_pages(tmp); munlock_vma_pages_all(tmp);
int error = __split_vma(mm, last, end, 1); if (error)
return error;
算法伪代码: do_munmap() {