欢迎进入Linux社区论坛,与200万技术人员互动交流 >>进入
* make sure we exit gracefully rather than endlessly redo
* the fault:
*/
/*分配新的页表和页框*/
fault = handle_mm_fault(mm, vma, address, write ? FAULT_FLAG_WRITE : 0);
if (unlikely(fault & VM_FAULT_ERROR)) {
mm_fault_error(regs, error_code, address, fault);
return;
}
if (fault & VM_FAULT_MAJOR) {
tsk->maj_flt++;
perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0,
regs, address);
} else {
tsk->min_flt++;
perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0,
regs, address);
}
check_v8086_mode(regs, address, tsk);
up_read(&mm->mmap_sem);
}
__bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code,
unsigned long address, int si_code)
{
struct task_struct *tsk = current;
/* User mode accesses just cause a SIGSEGV */
/*错误发生在用户态,则向用户进程发送一个SIGSEG信号V*/
if (error_code & PF_USER) {
/*
* It’s possible to have interrupts off here:
*/
local_irq_enable();
/*
* Valid to do another page fault here because this one came
* from user space:
*/
if (is_prefetch(regs, error_code, address))
return;
if (is_errata100(regs, address))
return;
if (unlikely(show_unhandled_signals))
show_signal_msg(regs, error_code, address, tsk);
/* Kernel addresses are always protection faults: */
tsk->thread.cr2 = address;
tsk->thread.error_code = error_code | (address >= TASK_SIZE);
tsk->thread.trap_no = 14;
force_sig_info_fault(SIGSEGV, si_code, address, tsk);
return;
}
…
…
}
在确定了这次异常是因为物理页没分配而导致后,就通过good_area路径来处理,可想而知,该路径在确定了访问权限足够后,将完成页表和物理页的分配,这个任务有handle_mm_fault()函数来完成
int handle_mm_fault(struct mm_struct *mm, struct vm_area_struct *vma,
unsigned long address, unsigned int flags)
{
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
__set_current_state(TASK_RUNNING);
count_vm_event(PGFAULT);
if (unlikely(is_vm_hugetlb_page(vma)))
return hugetlb_fault(mm, vma, address, flags);
pgd = pgd_offset(mm, address);
pud = pud_alloc(mm, pgd, address);//分配pud目录
if (!pud)
return VM_FAULT_OOM;
pmd = pmd_alloc(mm, pud, address);//分配pmd目录
if (!pmd)
return VM_FAULT_OOM;
pte = pte_alloc_map(mm, pmd, address);//分配pte表
if (!pte)
return VM_FAULT_OOM;
/*handle_pte_fault()的任务就是为pte绑定新的页框,它会根据pte页表项的情况来做不同的处理*/
return handle_pte_fault(mm, vma, address, pte, pmd, flags);
}
handle_pte_fault()函数的处理比较复杂,因为它要根据pte页表项对应的物理页的不同状态来做各种不同的处理,具体的分析以后再给出。
[1][2]
肯承认错误则错已改了一半