2.4越界访问
linux中的虚拟地址通过PGD,PTE等映射到物理地址。但当这个映射过程无法正常映射时候,就会报错,产生page fault exception。那么什么时候会无法正常呢?
- 编程错误。程序使用了不存在的地址
- 不是编程错误,linux的请求调页机制。即:当进程运行时,linux并不将全部的资源分配给进程,而是仅分配当前需要的这一部分,当进程需要另外的资源的时候(这时候就会产生缺页异常),linux再分配这部分。
编程错误linux肯定不会手软的,直接弄死进程。请求调页机制,linux会申请页的。
当MMU对不存在的虚拟地址进行映射的时候,会产生异常,__dabt_usr: __dabt_svc。 他们都会调用do_DataAbort。
__dabt_usr:
usr_entry
kuser_cmpxchg_check
@
@ Call the processor-specific abort handler:
@
@ r2 - aborted context pc
@ r3 - aborted context cpsr
@
@ The abort handler must return the aborted address in r0, and
@ the fault status register in r1. //说明了调用do_DataAbort时候传递给他的参数。 r0,r1,
@
#ifdef MULTI_DABORT
ldr r4, .LCprocfns
mov lr, pc
ldr pc, [r4, #PROCESSOR_DABT_FUNC]
#else
bl CPU_DABORT_HANDLER
#endif
@
@ IRQs on, then call the main handler
@
enable_irq
mov r2, sp //参数r2
adr lr, ret_from_exception
b do_DataAbort //调用do_DataAbort
ENDPROC(__dabt_usr)
do_DataAbor根据川进来的参数调用 inf->fn ,在这里就是 do_page_fault
asmlinkage void __exception
do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
{
const struct fsr_info *inf = fsr_info + (fsr & 15) + ((fsr & (1 << 10)) >> 6);
struct siginfo info;
if (!inf->fn(addr, fsr, regs))
return;
printk(KERN_ALERT "Unhandled fault: %s (0x%03x) at 0x%08lx\n",
inf->name, fsr, addr);
info.si_signo = inf->sig;
info.si_errno = 0;
info.si_code = inf->code;
info.si_addr = (void __user *)addr;
arm_notify_die("", regs, &info, fsr, 0);
}
do_page_fault函数是页异常的重要函数:
ps:因为在,__dabt_usr: __dabt_svc这两种情况下都会调用do_DataAbort函数,然后调用do_page_fault,所以调用do_page_fault可能是在内核空间,也可能是在用户空间。在内核空间可能会是 进程通过系统调用,中断等进入的,也可能进程本来是内核线程。所以在do_page_fault中要行进判断的。到底是从哪里发生的异常。
一下函数的分析大部分参考了《深入linux内核》书中的 第9章。
static int __kprobes //__kprobes标志应该是调试的一种东西,等以后再专门研究一下。
do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
{
struct task_struct *tsk;
struct mm_struct *mm;
int fault, sig, code;
if (notify_page_fault(regs, fsr)) //这个函数是专门和上面的__kprobes对应的,调试的东西
return 0;
tsk = current;
mm = tsk->mm; //将出现页异常的进程的 的进程描述符 赋给tsk,内存描述符赋给mm
/*
* If we're in an interrupt or have no user
* context, we must not take the fault..
*/
if (in_atomic() || !mm) //判断发生发生异常的,in_atomic判断是否是在 原子操作中:中断程序,可延迟函数,禁用内核抢占的临界区。 !mm 判断进程是否是内核线程。参照博客线程调度的文章。
goto no_context; //如果缺页是发生在这些情况下,那么就要特