📄 001_arch_i386_mm_fault_.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_42gxpn5gdj:7"> <br>
<div style=TEXT-ALIGN:center>
<table border=0 cellpadding=3 cellspacing=0 id=qmlk style="TEXT-ALIGN:left; MARGIN-LEFT:auto; MARGIN-RIGHT:auto" width=768>
<tbody>
<tr>
<td width=100%>
arch/i386/fault.c<br>
页面异常, swap, address space, shmem, filemap<br>
<br>
<br>
从文件开始,而不拘泥于文件。 <br>
<br>
此文件内只有两个函数。<br>
<br>
1. __verify_write int __verify_write(const void * addr,
unsigned long size)<br>
<br>
此函数主要应用于检查用户传递的内存块是否"可写". 可写意味着:<br>
a)内核容许其写,即vma的记录;<br>
b)相应的物理页面已经分配,即虚存已经有了对应的映射. 主要使用者是<br>
access_ok (include/asm-i386/uaccess.h).<br>
<br>
access_ok 大量应用于和用户空间进行数据交换的时候检查用户传递的内<br>
存是否合法.以避免在内核中产生页面访问异常,被别有用心的程序破坏了"大<br>
好形势".<br>
<br>
下面是此函数,以及注释.<br>
/*<br>
* Ugly, ugly, but the goto's result in better assembly..<br>
* 在操作用户传递的内存时检查其有效性,如果容许则预先扩展其堆栈<br>
* 或者为虚拟内存分配物理页面<br>
*/<br>
int __verify_write(const void * addr, unsigned long size)<br>
{<br>
struct vm_area_struct * vma;<br>
unsigned long start = (unsigned long) addr;<br>
<br>
if (!size)<br>
return 1;<br>
<br>
vma = find_vma(current->mm,
start);//寻找vma满足start<vma->end<br>
if (!vma)<br>
goto bad_area; //访问非法地址<br>
if (vma->vm_start > start) //访问堆栈空洞,需要扩展用户堆栈<br>
goto check_stack;<br>
<br>
good_area:<br>
if (!(vma->vm_flags & VM_WRITE))<br>
goto bad_area;<br>
size--;<br>
size += start & ~PAGE_MASK;<br>
size >>= PAGE_SHIFT;<br>
start &= PAGE_MASK;<br>
<br>
for (;;) {<br>
if
(handle_mm_fault(current->mm, vma, start, 1) <= 0)//检查page
table-><br>
goto
bad_area; //page dir->page本身, 有任何没有分配的页面,即刻分配物理<br>
if
(!size) //页面<br>
break;<br>
size--;<br>
start += PAGE_SIZE;<br>
if (start < vma->vm_end)<br>
continue;<br>
vma = vma->vm_next;<br>
if (!vma || vma->vm_start !=
start)<br>
goto bad_area;<br>
if (!(vma->vm_flags &
VM_WRITE))<br>
goto bad_area;;<br>
}<br>
return 1;<br>
<br>
check_stack:<br>
if (!(vma->vm_flags & VM_GROWSDOWN))
//必须是堆栈(或者有VM_GROWSDOWN属性)<br>
goto bad_area;<br>
if (expand_stack(vma, start) == 0) //只是扩展其vma中的地址范围<br>
goto good_area;
//还要给扩展的堆栈分配物理页面<br>
<br>
bad_area:<br>
return 0;<br>
}<br>
<br>
就这个函数本身,其中的注释已经足以理解其逻辑. 需要着重强调的是:<br>
a) vma<br>
vma是内核管理虚拟内存的手段,其中记录了进程所拥有的虚拟地址的<br>
范围和属性,是处理内存异常的依据之一. 如果进程所有的虚存较多,<br>
vma还可以组织成平衡树,以加快查找速度,不过这是vma资源的管理算法,<br>
而不是内核的逻辑. 逻辑和具体资源管理算法应该区别对待,以免陷入<br>
万劫不复的细节中去.所以, find_vma 本身的细节不用去追究,否则分<br>
析之粒度就过于详细,反而迷失于内核之中了.<br>
<br>
b) handle_mm_fault (mm/memory.c)<br>
此函数其使用在这里是"不务正业"的, 使用在本文件的下一个函数<br>
do_page_fault才是正道.其逻辑是比较简单的. 内核假定有三级页面映<br>
射,此函数顺着指定地址摸下去将涉及到的page table entry, page
dir<br>
entry, 逐个检查一遍如有未分配之page dir , page,
就分配一个并设<br>
置好相应的entry.其复杂之处在于<br>
handle_mm_fault->handle_pte_fault(也在memory.c之中):<br>
先来注释一把:<br>
<br>
/* 此段不翻译了,我们不是在翻译内核源码.<br>
* These routines also need to handle stuff like marking pages
dirty<br>
* and/or accessed for architectures that don't do it in hardware
(most<br>
* RISC architectures). The early dirtying is also good on
the i386.<br>
*<br>
* There is also a hook called "update_mmu_cache()" that
architectures<br>
* with external mmu caches can use to update those (ie the Sparc
or<br>
* PowerPC hashed page tables that act as extended TLBs).<br>
*<br>
* Note the "page_table_lock". It is to protect against kswapd
removing<br>
* pages from under us. Note that kswapd only ever _removes_ pages,
never<br>
* adds them. As such, once we have noticed that the page is not
present,<br>
* we can drop the lock early.<br>
*<br>
* The adding of pages is protected by the MM semaphore (which we
hold),<br>
* so we don't need to worry about a page being suddenly been added
into<br>
* our VM.<br>
*/<br>
static inline int handle_pte_fault(struct mm_struct *mm,<br>
struct vm_area_struct * vma, unsigned long address,<br>
int write_access, pte_t * pte)<br>
{<br>
pte_t entry;<br>
<br>
/*<br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -