Linux缺页异常处理--用户空间

上一篇 / 下一篇  2012-08-17 10:07:08 / 个人分类:Linux

51Testing软件测试网g^.S s?0@C

  用户空间的缺页异常可以分为两种情况--

+g+q$b"g%b(\f0

{-t }1Ht;Q0  1、触发异常的线性地址处于用户空间的vma中,但还未分配物理页,如果访问权限OK的话内核就给进程分配相应的物理页了

+g0V-{8}&d0

#pw0gl.Fp)wqM,O0  2、触发异常的线性地址不处于用户空间的vma中,这种情况得判断是不是因为用户进程的栈空间消耗完而触发的缺页异常,如果是的话则在用户空间对栈区域进行扩展,并且分配相应的物理页,如果不是则作为一次非法地址访问来处理,内核将终结进程

7]%@pW(w+p*m0

fI HK0KHN0  下面来看do_page_fault()函数对用户空间缺页异常的处理

J yfN-Q&jJ}0

x#? W-E1b-O)|0

\-pTZnqVH0
51Testing软件测试网,s!U r K.oCM-vm

dotraplinkage void __kprobes
T4_#fR%UrB0do_page_fault(struct pt_regs *regs, unsigned long error_code)
)y*A.?9`[0{
q j4{ Dz7AF0 struct vm_area_struct *vma;
alDX3j~(t0 struct task_struct *tsk;
yLN?hgg0 unsigned long address;51Testing软件测试网GHV _$ej q
 struct mm_struct *mm;
Ai$g9j,c~}*N+Z8m0 int write;
uo~*LqNKB0 int fault;

$tzJr2`;Fs7S#x0 51Testing软件测试网)Z:hz'u ala

 tsk = current; //获取当前进程
&S\AOy2n-D:G0 mm = tsk->mm;  //获取当前进程的地址空间
51Testing软件测试网 |c3XT-g { ~

51Testing软件测试网6j txZa%X

 /* Get the faulting address: */51Testing软件测试网r7QT)};Hv
 address = read_cr2(); //读取CR2寄存器获取触发异常的访问地址

W1m'tF Z1gB%yP0

!w}i*AF9g0 ...
}W#V)V {z[+p @0         ...
6lr2N\C CN0         ...51Testing软件测试网Jxa)zL.?
         ...
51Testing软件测试网|w'c1PH Tr

H#J U?Xw0 vma = find_vma(mm, address);//试图寻找到一个离address最近的vma,vma包含address或在address之后

;j&q;C f5n7u(Y`(b0

;Mw4p$l,qXx#a0 /*没有找到这样的vma则说明address之后没有虚拟内存区域,因此该address肯定是无效的,
5yA%z~Lsz0   通过bad_area()路径来处理,bad_area()的主体就是__bad_area()-->bad_area_nosemaphore()*/51Testing软件测试网 P_s/fu~`1@H
 if (unlikely(!vma)) {51Testing软件测试网 n0J$qC5pB
  bad_area(regs, error_code, address);51Testing软件测试网~/v#se1Q|${9Zs I]9dd
  return;
G/NS Rm8Q0 }
E$j6h1Q:_0 /*如果该地址包含在vma之中,则跳转到good_area处进行处理*/51Testing软件测试网(Qw8\]@M
 if (likely(vma->vm_start <= address))51Testing软件测试网8K\Fb7u mD*H
  goto good_area;

O:W fb9l]+jL@!L|0

$E,W[v1i2]b vs H5|0 /*不是前面两种情况的话,则判断是不是由于用户堆栈所占的页框已经使用完,而一个PUSH指令
?2iUhf3n%p-M;S0   引用了一个尚未和页框绑定的虚拟内存区域导致的一个异常,属于堆栈的虚拟内存区,其VM_GROWSDOWN位
I7wxzoy v0   被置位*/
U fr#[j*jx(Q0 if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) {
e1@f,W K5l7K0  bad_area(regs, error_code, address);//不是堆栈区域,则用bad_area()来处理51Testing软件测试网&K#AY [2l{gG*k-y
  return;
;Fz&d0n| E0 }
4B,rV:\!d?0 if (error_code & PF_USER) {//必须处于用户空间51Testing软件测试网6y{ C$BFh![2j
  /*51Testing软件测试网?8e$L'~s
   * Accessing the stack below %sp is always a bug.
J%_a!c^F&j'bO4i0   * The large cushion allows instructions like enter51Testing软件测试网.GKl@~n~3a T
   * and pusha to work. ("enter $65535, $31" pushes
;Y%zT xO a ?4T1c _0   * 32 pointers and then decrements %sp by 65535.)
%M#fCQr$^8x2j0   */51Testing软件测试网"x&E M w Eil4O
   /*这里检查address,只有该地址足够高(和堆栈指针的差不大于65536+32*sizeof(unsigned long)),51Testing软件测试网_|XQzm(D
     才能允许用户进程扩展它的堆栈地址空间,否则bad_area()处理*/
+D+hs W dq(E/Mk0  if (unlikely(address + 65536 + 32 * sizeof(unsigned long) < regs->sp)) {
pR@+F oxQ#n0   bad_area(regs, error_code, address);51Testing软件测试网$S2`s5|D ?0a*j7z
   return;51Testing软件测试网D7PWhM5L&u
  }
W'Y)YFt1\([hZfq[0 }51Testing软件测试网oe-D%R4x9T
 if (unlikely(expand_stack(vma, address))) {//堆栈扩展不成功同样由bad_area()处理
)k1oH ~@0  bad_area(regs, error_code, address);
V%DsyVo z}0  return;
kt*@ l(W.n r&xA0 }
51Testing软件测试网0E#W!n I0_-G2P*]

51Testing软件测试网z-l3fV3k{-yP

 /*
N&f c'Z8MyQ2{0  * Ok, we have a good vm_area for this memory access, so51Testing软件测试网ky#L4X*?2_3K
  * we can handle it..
5^*F3l^2BCv!`c3t0  */51Testing软件测试网z1XE!v4|Wb N
good_area:51Testing软件测试网 Bg FGI+z%PKI
 write = error_code & PF_WRITE;

o1K#Z5c6?-z.A:j ik:G0

6XL+w%E)V fD0 /*访问权限不够则通过bad_area_access_error()处理,该函数是对__bad_area()的封装,只不过
P,TTg#AM+O1D,y0   发送给用户进程的信号为SEGV_ACCERR*/51Testing软件测试网0z i gr G6y v
 if (unlikely(access_error(error_code, write, vma))) {
#q9\;i.a a0  bad_area_access_error(regs, error_code, address);
aXZTM$yItQ:u0i0  return;51Testing软件测试网3M2zL*uG
 }

)s'i]YT0 51Testing软件测试网/@([!XGn R9u h

 /*
^ J_MHu0c"}0  * If for any reason at all we couldn't handle the fault,
Q^Xcs0  * make sure we exit gracefully rather than endlessly redo51Testing软件测试网 FD2w]s3X
  * the fault:51Testing软件测试网 T ?%o7Pu
  */
GL9Lf;tH'}"v$z4e0  /*分配新的页表和页框*/
%o5bBb)pP`1fF_0 fault = handle_mm_fault(mm, vma, address, write ? FAULT_FLAG_WRITE : 0);

N_&KK/KZ*vu-Y0 51Testing软件测试网 bL i V `!D2R'M

 if (unlikely(fault & VM_FAULT_ERROR)) {51Testing软件测试网:T|gn6dq"I(e
  mm_fault_error(regs, error_code, address, fault);
$rQRS1h0  return;51Testing软件测试网BDrUnL1h
 }
51Testing软件测试网#R+h+V+R"j

51Testing软件测试网 Zo#A}jTs*g}

 if (fault & VM_FAULT_MAJOR) {
B0u`/H8AbFv eI0  tsk->maj_flt++;51Testing软件测试网%sj0vM5\#PG u
  perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0,
L+m*xI6\'?&a0         regs, address);51Testing软件测试网 ek:d.L#q-v
 } else {
s$gw}:E#m0  tsk->min_flt++;
s/ajQ!R^-shc0  perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0,51Testing软件测试网\u/f5DHQ7^ t:`
         regs, address);51Testing软件测试网8n_|N n5BDb+H{
 }
51Testing软件测试网 nz9x5oiT,i

51Testing软件测试网y7`en"f

 check_v8086_mode(regs, address, tsk);

h-U[;? `0 51Testing软件测试网z1iZL:yl

 up_read(&mm->mmap_sem);
Bct oU8l0}
51Testing软件测试网2{.D J"G(\ {m w'c/X


)\D[+}XRp\0
51Testing软件测试网$xwp)GH

  bad_area()函数的主体函数为__bad_area()-->__bad_area_nosemaphore(),这个函数在上一篇博文中分析了其对内核的非法访问的处理,现在看其对用户空间的非法访问的处理

Ej(lC5W'| O4C0

u#G3[kC)dc7@3B+B0

V)U\a@ j.G$@ |0

G-M#O Ho/[.z.`Gm0__bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code,51Testing软件测试网 L:xX(`)T `
         unsigned long address, int si_code)51Testing软件测试网5t8u?8kD%el)O/~'l
{
,n3E{0Ao+bb:|c'Vg0 struct task_struct *tsk = current;

As"is!\DR0

3B#_/~H%NWy ^ H0 /* User mode accesses just cause a SIGSEGV */51Testing软件测试网jGa;J(? nQ(J
 /*错误发生在用户态,则向用户进程发送一个SIGSEG信号V*/51Testing软件测试网YZ r1S2W'{)X
 if (error_code & PF_USER) {
5FR&Vo!H{&a[0  /*
z6I?]4v`6?0   * It's possible to have interrupts off here:
j%z/`CM \0   */
j1Gkj ^5Q0  local_irq_enable();
51Testing软件测试网d(YnD/Jmj

51Testing软件测试网2?6ZD` y,Ms d;n P'}

  /*
6e5|ev+?0   * Valid to do another page fault here because this one came51Testing软件测试网nh b1Mu
   * from user space:51Testing软件测试网|'D9{[b:a3m1W?
   */
!qvT$v+@ML8B0  if (is_prefetch(regs, error_code, address))51Testing软件测试网2T^8a9qk#m
   return;
51Testing软件测试网*~j|*i8\LN

51Testing软件测试网%N#RQB8F$x8O

  if (is_errata100(regs, address))51Testing软件测试网hU$h:W['x
   return;
51Testing软件测试网 ]5cCxL\

.wY {-a4a9^(q ]0  if (unlikely(show_unhandled_signals))
U5m J'RDg1A)B0   show_signal_msg(regs, error_code, address, tsk);

1o)cwRD3J7D0 51Testing软件测试网FV];d^+j@.a

  /* Kernel addresses are always protection faults: */51Testing软件测试网'mdN.lKcFR
  tsk->thread.cr2  = address;
xHg;V:C0  tsk->thread.error_code = error_code | (address >= TASK_SIZE);
yv,l~5fl7D3d0  tsk->thread.trap_no = 14;

EM ~YF?0 51Testing软件测试网 E2`!|&^o0rk

  force_sig_info_fault(SIGSEGV, si_code, address, tsk);51Testing软件测试网V7~jXU+jY

K2coUk4A @-D0  return;51Testing软件测试网 z \0z&`to*P s
 }51Testing软件测试网0S6tU h/W:n5a0];YN(b `
 ...
"Pw^ }!}4K AU0 ...
a!WPLKj0}

xUbS2Ccx051Testing软件测试网~&L#L kBW5h5HVHM1`%G1_

  在确定了这次异常是因为物理页没分配而导致后,就通过good_area路径来处理,可想而知,该路径在确定了访问权限足够后,将完成页表和物理页的分配,这个任务有handle_mm_fault()函数来完成

9j9Y]3Jh9tMFt0

{*GGw&]po'e0 51Testing软件测试网 d~;veX*S

51Testing软件测试网J\"|xHd

int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,51Testing软件测试网1g Tv,cx1b&e+s Q`
  unsigned long address, unsigned int flags)
RxNi&Q@yc0{
2O1[F C,LzE]0 pgd_t *pgd;
_ Z#Sy\b*TF0 pud_t *pud;51Testing软件测试网3~ B8S-uX4YJ7K
 pmd_t *pmd;
Z-RPDc4wR$J3\{B0 pte_t *pte;

eVmm#^0VP O3U!Y0 51Testing软件测试网5lEl{J7{z

 __set_current_state(TASK_RUNNING);51Testing软件测试网q&b9`7N-n9Nk z%h7P

51Testing软件测试网{Sr`!m$v`9l$s+r3A

 count_vm_event(PGFAULT);51Testing软件测试网(h k.}/i)J

51Testing软件测试网aA+| v B

 if (unlikely(is_vm_hugetlb_page(vma)))51Testing软件测试网b6FI9Id$\y
  return hugetlb_fault(mm, vma, address, flags);
51Testing软件测试网8J&j*\ M;||}

A#S*NT,aY|0 pgd = pgd_offset(mm, address);51Testing软件测试网;ft|`MiZv
 pud = pud_alloc(mm, pgd, address);//分配pud目录51Testing软件测试网^;w S ^|W#@+_s(J
 if (!pud)51Testing软件测试网t%m6JG [kVe
  return VM_FAULT_OOM;
5_%e&ujI b0 pmd = pmd_alloc(mm, pud, address);//分配pmd目录51Testing软件测试网pm7V)jv4Al
 if (!pmd)51Testing软件测试网Q bh9J$w\xe
  return VM_FAULT_OOM;51Testing软件测试网!A;h/u7r:R.{I0M:Q
 pte = pte_alloc_map(mm, pmd, address);//分配pte表51Testing软件测试网 j#s[#~2g%b
 if (!pte)
)^\5c Yl0  return VM_FAULT_OOM;
51Testing软件测试网rqX.GK-S T7a

#^h jR8R k0 /*handle_pte_fault()的任务就是为pte绑定新的页框,它会根据pte页表项的情况来做不同的处理*/
)b!U6I'g;AJ^0 return handle_pte_fault(mm, vma, address, pte, pmd, flags);51Testing软件测试网 Jo5Y8t%f0k u&t"S
}
51Testing软件测试网R)D8SH[ d

51Testing软件测试网br MBS2K

  handle_pte_fault()函数的处理比较复杂,因为它要根据pte页表项对应的物理页的不同状态来做各种不同的处理,具体的分析以后再给出。

C5Z$bb0o a8g!h&X Q \0

TAG:

 

评分:0

我来说两句

Open Toolbar