天天看點

linux缺頁異常處理,Linux缺頁異常處理--使用者空間

dotraplinkage void __kprobes

do_page_fault(struct pt_regs *regs, unsigned long error_code)

{

struct vm_area_struct *vma;

struct task_struct *tsk;

unsigned long address;

struct mm_struct *mm;

int write;

int fault;

tsk = current; //擷取目前程序

mm = tsk->mm;  //擷取目前程序的位址空間

address = read_cr2(); //讀取CR2寄存器擷取觸發異常的通路位址

...

...

...

...

vma = find_vma(mm, address);//試圖尋找到一個離address最近的vma,vma包含address或在address之後

if (unlikely(!vma)) {

bad_area(regs, error_code, address);

return;

}

if (likely(vma->vm_start <= address))

goto good_area;

if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) {

bad_area(regs, error_code, address);//不是堆棧區域,則用bad_area()來處理

return;

}

if (error_code & PF_USER) {//必須處于使用者空間

if (unlikely(address + 65536 + 32 * sizeof(unsigned long) < regs->sp)) {

bad_area(regs, error_code, address);

return;

}

}

if (unlikely(expand_stack(vma, address))) {//堆棧擴充不成功同樣由bad_area()處理

bad_area(regs, error_code, address);

return;

}

good_area:

write = error_code & PF_WRITE;

if (unlikely(access_error(error_code, write, vma))) {

bad_area_access_error(regs, error_code, address);

return;

}

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);

}