整個缺頁異常的處理過程非常復雜,我們這裡只簡單介紹一下缺頁涉及到的內核函數。
當CPU產生一個異常時,將會跳轉到異常處理的整個處理流程中。對於缺頁異常,CPU將跳轉到page_fault異常處理程序中,該異常處理程序會調用do_page_fault()函數,該函數通過讀取CR2寄存器獲得引起缺頁的線性地址,通過各種條件判斷以便確定一個合適的方案來處理這個異常。
do_page_fault()該函數通過各種條件來檢測當前發生異常的情況,但至少do_page_fault()會區分出引發缺頁的兩種情況:由編程錯誤引發異常,以及由進程地址空間中還未分配物理內存的線性地址引發。對於後一種情況,通常還分為用戶空間所引發的缺頁異常和內核空間引發的缺頁異常。內核引發的異常是由vmalloc()產生的,它只用於內核空間內存的分配。我們這裡需要關注的是用戶空間所引發的異常情況。這部分工作從do_page_fault()中的good_area標號處開始執行,主要通過handle_mm_fault()完成。
handle_mm_fault()該函數的主要功能是為引發缺頁的進程分配一個物理頁框,它先確定與引發缺頁的線性地址對應的各級頁目錄項是否存在,如何不存在則分進行分配。具體如何分配這個頁框是通過調用handle_pte_fault()完成的。
handle_pte_fault()該函數根據頁表項pte所描述的物理頁框是否在物理內存中,分為兩大類:請求調頁:被訪問的頁框不再主存中,那麼此時必須分配一個頁框。寫時復制:被訪問的頁存在,但是該頁是只讀的,內核需要對該頁進行寫操作,此時內核將這個已存在的只讀頁中的數據復制到一個新的頁框中。用戶進程訪問由malloc()分配的內存空間屬於第一種情況。對於請求調頁,handle_pte_fault()仍然將其細分為三種情況:
1.如果頁表項確實為空(pte_none(entry)),那麼必須分配頁框。如果當前進程實現了vma操作函數集合中的fault鉤子函數,那麼這種情況屬於基於文件的內存映射,它調用do_linear_fault()進行分配物理頁框。否則,內核將調用針對匿名映射分配物理頁框的函數do_anonymous_page()。
2.如果檢測出該頁表項為非線性映射(pte_file(entry)),則調用do_nonlinear_fault()分配物理頁。
3.如果頁框事先被分配,但是此刻已經由主存換出到了外存,則調用do_swap_page()完成頁框分配。
在以上三個函數中缺頁異常處理函數通過alloc_zeroed_user_highpage_movable()來完成物理頁的分配過程。alloc_zeroed_user_highpage_movable()函數最終調用了alloc_pages()。 經過這樣一個復雜的過程,用戶進程所訪問的線性地址終於對應到了一塊物理內存。
參考:
1.《深入理解Linux內核》 PDF 下載 http://www.linuxidc.com/Linux/2011-08/41228.htm
2.《深入Linux內核架構》 PDF 下載見 http://www.linuxidc.com/Linux/2012-06/62984.htm