📄 013_mm_mremap_c.html
字号:
<html lang="zh-CN" xmlns:gdoc=""> <head> <meta content="text/html; charset=utf-8" http-equiv="Content-Type"> <style type="text/css">/* default css */table { font-size: 1em; line-height: inherit;}div, address, ol, ul, li, option, select { margin-top: 0px; margin-bottom: 0px;}p { margin: 0px;}body { margin: 0px; padding: 0px; font-family: Verdana, sans-serif; font-size: 10pt; background-color: #ffffff;}h6 { font-size: 10pt }h5 { font-size: 11pt }h4 { font-size: 12pt }h3 { font-size: 13pt }h2 { font-size: 14pt }h1 { font-size: 16pt }blockquote {padding: 10px; border: 1px #DDD dashed }a img {border: 0}div.google_header, div.google_footer { position: relative; margin-top: 1em; margin-bottom: 1em;}/* end default css */ /* default print css */ @media print { body { padding: 0; margin: 0; } div.google_header, div.google_footer { display: block; min-height: 0; border: none; } div.google_header { flow: static(header); } /* used to insert page numbers */ div.google_header::before, div.google_footer::before { position: absolute; top: 0; } div.google_footer { flow: static(footer); } /* always consider this element at the start of the doc */ div#google_footer { flow: static(footer, start); } span.google_pagenumber { content: counter(page); } span.google_pagecount { content: counter(pages); } } @page { @top { content: flow(header); } @bottom { content: flow(footer); } } /* end default print css */ /* custom css *//* end custom css */ /* ui edited css */ body { font-family: Verdana; font-size: 10.0pt; line-height: normal; background-color: #ffffff; } .documentBG { background-color: #ffffff; } /* end ui edited css */</style> </head> <body revision="dcbsxfpf_54cr593rcq:5"> <table align=center cellpadding=0 cellspacing=0 height=5716 width=768>
<tbody>
<tr>
<td height=5716 valign=top width=100%>
<pre>2006-5-24 <br>mm/mremap.c<br><br> 本模块提供系统调用,mremap。请先参考对模块mm/mmap.c的分析。先看系统调<br>用的功能。<br> sys_mremap(unsigned long addr,unsigned long old_len, unsigned long new_len,<br> unsigned long flags, unsigned long new_addr)<br> <br> mremap扩展或者收缩现存的memory mapping。可能会将映射在虚拟地址内进行移动。<br> addr是要进行remap的现存映射首地址。old_size是现存映射的大小.new_len是<br>这次remap的请求长度。<br> 利用mremap可以实现高效的无拷贝realloc。<br> flags:<br> MREMAP_MAYMOVE:容许配给一个新的虚拟地址.<br> MREMAP_FIXED: 配合new_addr,仅当此标记置位,new_addr才有意义.<br> <br> 对这系统调用的分析,我们采用代码的强注释来完成:<br> <br>/* 分为三种情况阅读代码比较容易或得整体印象:<br> * start --end(start+len) 位于vma的起始,结束,末尾<br> * 加上每种又分为扩展和收缩两种情况。<br> */<br>/*注意如果是fixmap,新旧地址就不能相等,并且<br> *新旧地址交叠也不容许。(除非新旧长度都是0)<br> */<br>unsigned long do_mremap(unsigned long addr,unsigned long old_len, <br>unsigned long new_len,unsigned long flags, unsigned long new_addr)<br>{<br> struct vm_area_struct *vma;<br> unsigned long ret = -EINVAL;<br><br> if (flags & ~(MREMAP_FIXED | MREMAP_MAYMOVE))//仅支持这两个标记<br> goto out;<br><br> if (addr & ~PAGE_MASK)//首地址需要page align<br> goto out;<br><br> old_len = PAGE_ALIGN(old_len);<br> new_len = PAGE_ALIGN(new_len);<br><br> /* new_addr is only valid if MREMAP_FIXED is specified */<br> //如果要求fix remap,首先清除指定(新)地址上的映射<br> if (flags & MREMAP_FIXED) {//要求映射到指定虚拟地址<br> if (new_addr & ~PAGE_MASK)<br> goto out;<br> if (!(flags & MREMAP_MAYMOVE))//fixmap要求和move配合使用<br> goto out;<br><br> if (new_len > TASK_SIZE || new_addr > TASK_SIZE - new_len)<br> goto out;<br><br> /* Check if the location we're moving into overlaps the<br> * old location at all, and fail if it does.<br> */<br> //fix map要求新旧地址空间不能有任何重叠<br> if ((new_addr <= addr) && (new_addr+new_len) > addr)<br> goto out;<br><br> if ((addr <= new_addr) && (addr+old_len) > new_addr)<br> goto out;<br><br> do_munmap(current->mm, new_addr, new_len);/*参考对mmap.c的分析*/<br> }<br><br> /*<br> * Always allow a shrinking remap: that just unmaps<br> * the unnecessary pages..<br> */<br> //如果remap后映射缩减,需要unmap缩减部分<br> ret = addr;<br> if (old_len >= new_len) {<br> do_munmap(current->mm, addr+new_len, old_len - new_len);<br> if (!(flags & MREMAP_FIXED) || (new_addr == addr))<br> goto out;<br> }<br><br> /*<br> * Ok, we need to grow.. or relocate.<br> */<br> //寻找涉案vma<br> ret = -EFAULT;<br> vma = find_vma(current->mm, addr); //addr<vma_end<br> if (!vma || vma->vm_start > addr)<br> goto out;//no such vma contain "addr"<br><br> //检查指定vma 是否容许此次remap<br> /* We can't remap across vm area boundaries */<br> if (old_len > vma->vm_end - addr)//注意:remap 不能跨越vma<br> goto out; //注意用户指定的addr 可以在一个vma内,而不是vma_stat<br> if (vma->vm_flags & VM_DONTEXPAND) {//不能扩展的vma<br> if (new_len > old_len)<br> goto out;<br> }<br> if (vma->vm_flags & VM_LOCKED) {//内存锁定 总量不能超越限制<br> unsigned long locked = current->mm->locked_vm << PAGE_SHIFT;<br> locked += new_len - old_len;<br> ret = -EAGAIN;<br> if (locked > current->rlim[RLIMIT_MEMLOCK].rlim_cur)<br> goto out;<br> }<br> ret = -ENOMEM;<br> if ((current->mm->total_vm << PAGE_SHIFT) + (new_len - old_len)<br> > current->rlim[RLIMIT_AS].rlim_cur)//总vma不能超限<br> goto out;<br> /* Private writable mapping? Check memory availability.. */<br> /*内核为此种映射保留一定的内存,除非用户无需此种机制*/<br> if ((vma->vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE &&<br> !(flags & MAP_NORESERVE) &&<br> !vm_enough_memory((new_len - old_len) >> PAGE_SHIFT))<br> goto out;<br><br><br><br> /* old_len exactly to the end of the area..<br> * And we're not relocating the area.<br> */<br> if (old_len == vma->vm_end - addr &&<br> !((flags & MREMAP_FIXED) && (addr != new_addr)) &&<br> (old_len != new_len || !(flags & MREMAP_MAYMOVE))) {<br> //位于末尾,长度不等或者不容许移动<br> //并且不是非等首地址的fixmap<br> unsigned long max_addr = TASK_SIZE;<br> if (vma->vm_next)<br> max_addr = vma->vm_next->vm_start;<br> /* can we just expand the current mapping? */<br> if (max_addr - addr >= new_len) {<br> int pages = (new_len - old_len) >> PAGE_SHIFT;<br> spin_lock(&vma->vm_mm->page_table_lock);<br> vma->vm_end = addr + new_len;<br> spin_unlock(&vma->vm_mm->page_table_lock);<br> current->mm->total_vm += pages;<br> if (vma->vm_flags & VM_LOCKED) {<br> current->mm->locked_vm += pages;<br> make_pages_present(addr + old_len,<br> addr + new_len);<br> }<br> ret = addr;<br> goto out;<br> }<br> }<br><br> /*<br> * We weren't able to just expand or shrink the area,<br> * we need to create a new one and move it..<br> */<br> /*必须移动映射了*/<br> ret = -ENOMEM;<br> if (flags & MREMAP_MAYMOVE) {//必须容许移动<br> if (!(flags & MREMAP_FIXED)) {/*非fixmap时可以寻找新的虚拟空间*/<br> <br> new_addr = get_unmapped_area(0, new_len);<br> if (!new_addr)<br> goto out;<br> }<br> /*fixmap时new_addr指定的一段空间不会和old space交叠在一起所以move <br> vma没有问题*/<br> ret = move_vma(vma, addr, old_len, new_len, new_addr);<br> }<br>out:<br> return ret;<br>} <br><br>剩下的需要分析的函数就是move_vma,其他函数不再分析: <br>static inline unsigned long move_vma(struct vm_area_struct * vma,<br> unsigned long addr, unsigned long old_len, unsigned long new_len,<br> unsigned long new_addr)<br>{<br> struct vm_area_struct * new_vma;<br><br> new_vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);<br> if (new_vma) {<br> //移动页表包括分配新的页表,页目录,拷贝pte<br> if (!move_page_tables(current->mm, new_addr, addr, old_len)) {<br> //页表操作完成,建立新的vma<br> *new_vma = *vma;<br> new_vma->vm_start = new_addr;<br> new_vma->vm_end = new_addr+new_len;<br> new_vma->vm_pgoff += (addr - vma->vm_start) >> PAGE_SHIFT;<br> new_vma->vm_raend = 0;<br> if (new_vma->vm_file)<br> get_file(new_vma->vm_file);<br> if (new_vma->vm_ops && new_vma->vm_ops->open)<br> new_vma->vm_ops->open(new_vma);<br> insert_vm_struct(current->mm, new_vma);<br> do_munmap(current->mm, addr, old_len); //unmap老的地址空间<br> current->mm->total_vm += new_len >> PAGE_SHIFT;<br> if (new_vma->vm_flags & VM_LOCKED) {//lock就立即分配内存<br> current->mm->locked_vm += new_len >> PAGE_SHIFT;<br> make_pages_present(new_vma->vm_start,<br> new_vma->vm_end);<br> }<br> return new_addr;<br> }<br> kmem_cache_free(vm_area_cachep, new_vma);<br> }<br> return -ENOMEM;<br>}<br> <br><br> <br><br> <br> <br></pre>
</td>
</tr>
</tbody>
</table></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -