缺頁異常被觸發通常有兩種情況——
1.程序設計的不當導致訪問了非法的地址
2.訪問的地址是合法的,但是該地址還未分配物理頁框
下面解釋一下第二種情況,這是虛擬內存管理的一個特性。盡管每個進程獨立擁有3GB的可訪問地址空間,但是這些資源都是內核開出的空頭支票,也就是說進程手握著和自己相關的一個個虛擬內存區域(vma),但是這些虛擬內存區域並不會在創建的時候就和物理頁框掛鉤,由於程序的局部性原理,程序在一定時間內所訪問的內存往往是有限的,因此內核只會在進程確確實實需要訪問物理內存時才會將相應的虛擬內存區域與物理內存進行關聯(為相應的地址分配頁表項,並將頁表項映射到物理內存),也就是說這種缺頁異常是正常的,而第一種缺頁異常是不正常的,內核要采取各種可行的手段將這種異常帶來的破壞減到最小。
缺頁異常的處理函數為do_page_fault(),該函數是和體系結構相關的一個函數,缺頁異常的來源可分為兩種,一種是內核空間(訪問了線性地址空間的第4個GB),一種是用戶空間(訪問了線性地址空間的0~3GB),以X86架構為例,先來看內核空間異常的處理。
- 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; //獲取當前進程的地址空間
-
- /* Get the faulting address: */
- address = read_cr2(); //讀取CR2寄存器獲取觸發異常的訪問地址
-
- ...
- ...
-
- if (unlikely(fault_in_kernel_space(address))) { //判斷address是否處於內核線性地址空間
- if (!(error_code & (PF_RSVD | PF_USER | PF_PROT))) {//判斷是否處於內核態
- if (vmalloc_fault(address) >= 0)//處理vmalloc異常
- return;
-
- if (kmemcheck_fault(regs, address, error_code))
- return;
- }
-
- /* Can handle a stale RO->RW TLB: */
- /*異常發生在內核地址空間但不屬於上面的情況或上面的方式無法修正,
- 則檢查相應的頁表項是否存在,權限是否足夠*/
- if (spurious_fault(error_code, address))
- return;
-
- /* kprobes don't want to hook the spurious faults: */
- if (notify_page_fault(regs))
- return;
- /*
- * Don't take the mm semaphore here. If we fixup a prefetch
- * fault we could otherwise deadlock:
- */
- bad_area_nosemaphore(regs, error_code, address);
-
- return;
- }
- ...
- ...
- }