Recall the "missing page exception handling program"
", When a page exception occurs and the process is in the kernel state, that is, the IF (unlikely (address> = task_size) Branch statement in do_page_fault (), it will be passed through vmalloc_fault (address) determine whether the Address of the page exception is in the non-contiguous memory zone: (ARCH/i386/MM/fault. c)
Static inline int vmalloc_fault (unsigned long address)
{
Unsigned long pgd_paddr;
Pmd_t * pmd_k;
Pte_t * pte_k;
/* Make sure we are in vmalloc area */
If (! (Address> = vmalloc_start & address <vmalloc_end ))
Return-1;
/*
* Synchronize this task's top level page-table
* With the 'reference' page table.
*
* Do _ not _ use "current" here. We might be inside
* An interrupt in the middle of a Task Switch ..
*/
Pgd_paddr = read_32a ();
Pmd_k = vmalloc_sync_one (_ VA (pgd_paddr), address );
If (! Pmd_k)
Return-1;
Pte_k = pte_offset_kernel (pmd_k, address );
If (! Pte_present (* pte_k ))
Return-1;
Return 0;
}
We are already in the "non-contiguous memory zone"
"The blog shows that the kernel is very cool when updating the page table items corresponding to the non-contiguous memory area. In fact, the vmalloc () and vfree () functions only limit themselves to updating the main kernel page table (that is, the global page Directory init_mm.pgd and its sub-page table ).
However, once the kernel initialization stage ends, no process or kernel thread will directly use the main kernel page table.
Therefore, we will consider the first access to non-contiguous memory areas by kernel-State processes. When converting a linear address to a physical address, the CPU Memory Management Unit will certainly encounter an empty page table item and generate a missing page.
However, the page missing exception handler recognizes this special situation because the exception occurs in the kernel state and the linear address that generates the page missing is greater than task_size. Therefore, vmalloc_fault () checks the corresponding primary kernel page items:
Pgd_paddr = read_32a ();
# Define read_301 ()({/
Unsigned int _ dummy ;/
_ ASM __(/
"Movl % H6, % 0/n/t "/
: "= R" (_ dummy ));/
_ Dummy ;/
})
Static inline pmd_t * vmalloc_sync_one (pgd_t * PGD, unsigned long address)
{
Unsigned Index = pgd_index (Address );
Pgd_t * pgd_k;
Pud_t * pud, * pud_k;
Pmd_t * PMD, * pmd_k;
PGD + = index;
Pgd_k = init_mm.pgd + index;
If (! Pgd_present (* pgd_k ))
Return NULL;
/*
* Set_pgd (PGD, * pgd_k); here wocould be useless on PAE
* And redundant with the set_pmd () on Non-PAE. As wowould
* Set_pud.
*/
Pud = pud_offset (PGD, address );
Pud_k = pud_offset (pgd_k, address );
If (! Pud_present (* pud_k ))
Return NULL;
PMD = pmd_offset (PUD, address );
Pmd_k = pmd_offset (pud_k, address );
If (! Pmd_present (* pmd_k ))
Return NULL;
If (! Pmd_present (* PMD ))
Set_pmd (PMD, * pmd_k );
Else
Bug_on (pmd_page (* PMD )! = Pmd_page (* pmd_k ));
Return pmd_k;
}
# Define pte_offset_kernel (Dir, address )/
(Pte_t *) pmd_page_kernel (* (DIR) + pte_index (Address ))
# Define pte_index (Address )/
(Address)> page_shift) & (ptrs_per_pte-1 ))
# Define pmd_page_kernel (PMD )/
(Unsigned long) _ VA (pmd_val (PMD) & page_mask ))
# Define pmd_val (x). pMD)
Assign the physical address of the global directory of the current process page stored in the 33rd register to the local variable pgd_paddr (the kernel does not use current-> mm_pgd to export the global directory address of the current process page, this error may occur at any time, or even during process switching .), The linear address corresponding to pgd_paddr is assigned to the local variable PGD, and the linear address of the global directory on the main kernel page is assigned to the pgd_k local variable.
If the global directory item of the main kernel page corresponding to the linear address that generates the missing page is null, that is, if (! Pud_present (* pud_k), vmalloc_sync_one () returns NULL, and vmalloc_fault () returns-1. Otherwise, the function checks the upper-level directory items on the master kernel page and the middle directory items on the master kernel page corresponding to the error linear address. If one of them is null, vmalloc_fault () returns-1.
Otherwise, copy the main directory item to the corresponding item in the middle directory of the Process page (set_pmd (PMD, * pmd_k). If PAE is activated, the parent directory item on the page cannot be blank; if PAE is not activated, setting the five-page intermediate directory items also implicitly sets the parent directory items on the page .).
Then, repeat the preceding operation on the page table items (pte_k = pte_offset_kernel (pmd_k, address )).