mm/mremap.c
本模块提供系统调用,mremap。请先参考对模块mm/mmap.c的分析。先看系统调
用的功能。
sys_mremap(unsigned long addr,unsigned long old_len, unsigned long new_len,
unsigned long flags, unsigned long new_addr)
mremap扩展或者收缩现存的memory mapping。可能会将映射在虚拟地址内进行移动。
addr是要进行remap的现存映射首地址。old_size是现存映射的大小.new_len是
这次remap的请求长度。
利用mremap可以实现高效的无拷贝realloc。
flags:
MREMAP_MAYMOVE:容许配给一个新的虚拟地址.
MREMAP_FIXED: 配合new_addr,仅当此标记置位,new_addr才有意义.
对这系统调用的分析,我们采用代码的强注释来完成:
unsigned long do_mremap(unsigned long addr,unsigned long old_len,
unsigned long new_len,unsigned long flags, unsigned long new_addr)
{
struct vm_area_struct *vma;
unsigned long ret = -EINVAL;
if (flags & ~(MREMAP_FIXED | MREMAP_MAYMOVE)) goto out;
if (addr & ~PAGE_MASK) goto out;
old_len = PAGE_ALIGN(old_len);
new_len = PAGE_ALIGN(new_len); if (flags & MREMAP_FIXED) { if (new_addr & ~PAGE_MASK)
goto out;
if (!(flags & MREMAP_MAYMOVE)) goto out;
if (new_len > TASK_SIZE || new_addr > TASK_SIZE - new_len)
goto out; if ((new_addr <= addr) && (new_addr+new_len) > addr)
goto out;
if ((addr <= new_addr) && (addr+old_len) > new_addr)
goto out;
do_munmap(current->mm, new_addr, new_len);
} ret = addr;
if (old_len >= new_len) {
do_munmap(current->mm, addr+new_len, old_len - new_len);
if (!(flags & MREMAP_FIXED) || (new_addr == addr))
goto out;
} ret = -EFAULT;
vma = find_vma(current->mm, addr); if (!vma || vma->vm_start > addr)
goto out;
if (old_len > vma->vm_end - addr) goto out; if (vma->vm_flags & VM_DONTEXPAND) { if (new_len > old_len)
goto out;
}
if (vma->vm_flags & VM_LOCKED) { unsigned long locked = current->mm->locked_vm << PAGE_SHIFT;
locked += new_len - old_len;
ret = -EAGAIN;
if (locked > current->rlim[RLIMIT_MEMLOCK].rlim_cur)
goto out;
}
ret = -ENOMEM;
if ((current->mm->total_vm << PAGE_SHIFT) + (new_len - old_len)
> current->rlim[RLIMIT_AS].rlim_cur) goto out;
if ((vma->vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE &&
!(flags & MAP_NORESERVE) &&
!vm_enough_memory((new_len - old_len) >> PAGE_SHIFT))
goto out;
if (old_len == vma->vm_end - addr &&
!((flags & MREMAP_FIXED) && (addr != new_addr)) &&
(old_len != new_len || !(flags & MREMAP_MAYMOVE))) { unsigned long max_addr = TASK_SIZE;
if (vma->vm_next)
max_addr = vma->vm_next->vm_start;
if (max_addr - addr >= new_len) {
int pages = (new_len - old_len) >> PAGE_SHIFT;
spin_lock(&vma->vm_mm->page_table_lock);
vma->vm_end = addr + new_len;
spin_unlock(&vma->vm_mm->page_table_lock);
current->mm->total_vm += pages;
if (vma->vm_flags & VM_LOCKED) {
current->mm->locked_vm += pages;
make_pages_present(addr + old_len,
addr + new_len);
}
ret = addr;
goto out;
}
}
ret = -ENOMEM;
if (flags & MREMAP_MAYMOVE) { if (!(flags & MREMAP_FIXED)) {
new_addr = get_unmapped_area(0, new_len);
if (!new_addr)
goto out;
}
ret = move_vma(vma, addr, old_len, new_len, new_addr);
}
out:
return ret;
}
剩下的需要分析的函数就是move_vma,其他函数不再分析:
static inline unsigned long move_vma(struct vm_area_struct * vma,
unsigned long addr, unsigned long old_len, unsigned long new_len,
unsigned long new_addr)
{
struct vm_area_struct * new_vma;
new_vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
if (new_vma) { if (!move_page_tables(current->mm, new_addr, addr, old_len)) { *new_vma = *vma;
new_vma->vm_start = new_addr;
new_vma->vm_end = new_addr+new_len;
new_vma->vm_pgoff += (addr - vma->vm_start) >> PAGE_SHIFT;
new_vma->vm_raend = 0;
if (new_vma->vm_file)
get_file(new_vma->vm_file);
if (new_vma->vm_ops && new_vma->vm_ops->open)
new_vma->vm_ops->open(new_vma);
insert_vm_struct(current->mm, new_vma);
do_munmap(current->mm, addr, old_len); current->mm->total_vm += new_len >> PAGE_SHIFT;
if (new_vma->vm_flags & VM_LOCKED) { current->mm->locked_vm += new_len >> PAGE_SHIFT;
make_pages_present(new_vma->vm_start,
new_vma->vm_end);
}
return new_addr;
}
kmem_cache_free(vm_area_cachep, new_vma);
}
return -ENOMEM;
}