Transferred from: http://blog.csdn.net/vanbreaker/article/details/7870769
Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.
There are two types of fault pages in user space--
1. The linear address that triggered the exception is in the VMA of the user space, but the physical page is not assigned, and if Access is OK, the kernel assigns the corresponding physical page to the process.
2. The linear address of the triggering exception is not in the VMA of the user space, this situation is determined not because the user process stack space consumed by the page fault triggered by the exception, if it is in the user space to expand the stack area, and assign the corresponding physical page, if not as an illegal address access to deal with, The kernel will end the process
Here's how the Do_page_fault () function handles the error of user space pages
[CPP]View PlainCopy
- 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; //Get current process
- MM = tsk->mm; //Gets the address space of the current process
- /* Get the faulting address: */
- Address = Read_cr2 (); //Read the CR2 register to get the access address that triggered the exception
- ...
- ...
- ...
- ...
- VMA = FIND_VMA (mm, address); //Try to find a vma,vma that is closest to address contains address or after address
- / * Does not find such a VMA that there is no virtual memory area after address, so the address is definitely invalid,
- Handled by the Bad_area () path, the body of the Bad_area () is __bad_area ()-->bad_area_nosemaphore () */
- if (unlikely (!VMA)) {
- Bad_area (Regs, Error_code, address);
- return;
- }
- / * If the address is included in VMA, jump to good_area for processing * /
- if (likely (Vma->vm_start <= address))
- Goto Good_area;
- /* is not the first two cases, the decision is not due to the user stack occupied by the page box has been used, and a push instruction
- An exception that is referenced by a virtual memory region that has not yet been bound to the page box, which belongs to the virtual memory area of the stack, its vm_growsdown bit
- Be placed */
- if (Unlikely (!) ( Vma->vm_flags & Vm_growsdown )) {
- Bad_area (Regs, Error_code, address); //Is not a stack area, it is handled with Bad_area ()
- return;
- }
- if (Error_code & pf_user) {//must be in user space
- /*
- * Accessing the stack below%SP is always a bug.
- * The large cushion allows instructions like enter
- * and Pusha to work. ("Enter $65535, $31" pushes
- * Pointers and then decrements%sp by 65535.)
- */
- / * Here, only the address is high enough (the difference from the stack pointer is not greater than 65536+32*sizeof (unsigned long)),
- To allow the user process to extend its stack address space, otherwise bad_area () processing */
- if (unlikely (address + 65536 + * sizeof (unsigned long) < REGS->SP)) {
- Bad_area (Regs, Error_code, address);
- return;
- }
- }
- if (Unlikely (Expand_stack (VMA, address))) {//stack extension not successful also handled by Bad_area ()
- Bad_area (Regs, Error_code, address);
- return;
- }
- /*
- * Ok, we have a good vm_area for this memory access, so
- * We can handle it.
- */
- Good_area:
- Write = Error_code & pf_write;
- / * Insufficient access is handled by Bad_area_access_error (), which is the encapsulation of __bad_area (), except
- The signal sent to the user process is segv_accerr*/
- if (Unlikely (Access_error (Error_code, write, VMA))) {
- Bad_area_access_error (Regs, Error_code, address);
- return;
- }
- /*
- * If for any reason at all we couldn ' t handle the fault,
- * Make sure we exit gracefully rather than endlessly redo
- * The fault:
- */
- /* Assign a new Page table and Page box */
- 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);
- }
The body function of the Bad_area () function is __bad_area ()-->__bad_area_nosemaphore (), which analyzes its handling of illegal access to the kernel in its previous blog post, and now sees its handling of illegal access to user space
[CPP]View PlainCopy
- __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 * /
- / * error occurs in the user state, then a sigseg signal is sent to the user process v*/
- if (Error_code & Pf_user) {
- /*
- * It ' s possible to has interrupts off here:
- */
- Local_irq_enable ();
- /*
- * Valid to does 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 is 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;
- }
- ...
- ...
- }
After determining that the exception is due to the physical page is not assigned, and then through the Good_area path to processing, it is conceivable that the path in determining the access rights sufficient, will complete the page table and the physical page allocation, the task has Handle_mm_fault () function to complete
[CPP]View PlainCopy
- 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); //Assign the PUD directory
- if (!pud)
- return vm_fault_oom;
- PMD = Pmd_alloc (mm, pud, address); //Assign PMD directory
- if (!PMD)
- return vm_fault_oom;
- Pte = Pte_alloc_map (mm, PMD, address); //Allocation Pte Table
- if (!pte)
- return vm_fault_oom;
- the task of/*handle_pte_fault () is to bind the PTE with a new page box, which will be processed differently depending on the condition of the PTEs page table entry.
- return Handle_pte_fault (mm, VMA, address, PTE, PMD, flags);
- }
The processing of the Handle_pte_fault () function is complicated, because it has to do a variety of different processing according to the different state of the physical page corresponding to the PTE page table item, and the specific analysis is given later.
Linux page fault handling--User space "Go"