Read the fucking source code!
--By 魯迅A picture is worth a thousand words.
--By 高爾基說明:函數
上篇文章分析到malloc/mmap
函數中,內核實現只是在進程的地址空間創建好了vma
區域,並無實際的虛擬地址到物理地址的映射操做。這部分就是在Page Fault
異常錯誤處理中實現的。工具
Linux內核中的Page Fault
異常處理很複雜,涉及的細節也不少,malloc/mmap
的物理內存映射只是它的一個子集功能,下圖大概涵蓋了出現Page Fault
的狀況:spa
下邊就開始來啃啃硬骨頭吧。code
Page Fault
的異常處理,依賴於體系結構,所以有必要來介紹一下Arm64
的處理。
代碼主要參考:arch/arm64/kernel/entry.S
。blog
Arm64在取指令或者訪問數據時,須要把虛擬地址轉換成物理地址,這個過程須要進行幾種檢查,在不知足的狀況下都能形成異常:進程
從上圖中能夠看到,最後都會調到do_mem_abort
函數,這個函數比較簡單,直接看代碼,位於arch/arm64/mm/fault.c
:內存
/* * Dispatch a data abort to the relevant handler. */ asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr, struct pt_regs *regs) { const struct fault_info *inf = esr_to_fault_info(esr); struct siginfo info; if (!inf->fn(addr, esr, regs)) return; pr_alert("Unhandled fault: %s (0x%08x) at 0x%016lx\n", inf->name, esr, addr); mem_abort_decode(esr); info.si_signo = inf->sig; info.si_errno = 0; info.si_code = inf->code; info.si_addr = (void __user *)addr; arm64_notify_die("", regs, &info, esr); }
該函數中關鍵的處理:根據傳進來的esr
獲取fault_info
信息,從而去調用函數。struct fault_info
用於錯誤狀態下對應的處理方法,而內核中也定義了全局結構fault_info
,存放了全部的狀況。
主要的錯誤狀態和處理函數對應以下:it
static const struct fault_info fault_info[] = { { do_bad, SIGBUS, 0, "ttbr address size fault" }, { do_bad, SIGBUS, 0, "level 1 address size fault" }, { do_bad, SIGBUS, 0, "level 2 address size fault" }, { do_bad, SIGBUS, 0, "level 3 address size fault" }, { do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 0 translation fault" }, { do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 1 translation fault" }, { do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 2 translation fault" }, { do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 3 translation fault" }, { do_bad, SIGBUS, 0, "unknown 8" }, { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 1 access flag fault" }, { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 2 access flag fault" }, { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 access flag fault" }, { do_bad, SIGBUS, 0, "unknown 12" }, { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 1 permission fault" }, { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 2 permission fault" }, { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 permission fault" }, ... };
從代碼中能夠看出:io
do_translation_fault
,實際中do_translation_fault
最終也會調用到do_page_fault
;do_page_fault
;do_bad
,其中未列出來的部分還包括do_sea
等操做函數;
do_translation_fault
asm
do_page_fault
do_page_fault
函數爲頁錯誤異常處理的核心函數,與體系結構相關,上圖中的handle_mm_fault
函數爲通用函數,也就是無論哪一種處理器結構,最終都會調用到該函數。
handle_mm_fault
handle_mm_fault
用於處理用戶空間的頁錯誤異常:
do_page_fault
函數的流程圖中也能看出來,當觸發異常的虛擬地址屬於某個vma
,而且擁有觸發頁錯誤異常的權限時,會調用到handle_mm_fault
函數,而handle_mm_fault
函數的主要邏輯是經過__handle_mm_fault
來實現的。流程以下圖:
do_fault
do_fault
函數用於處理文件頁異常,包括如下三種狀況:
do_anonymous_page
匿名頁的缺頁異常處理調用本函數,在如下狀況下會觸發:
do_swap_page
若是訪問Swap頁面
出錯(頁面不在內存中),則從Swap cache
或Swap文件
中讀取該頁面。
因爲在4.14內核
版本中,do_swap_page
調用的不少函數都是空函數,沒法進一步的瞭解,大致的流程以下圖:
do_wp_page
do_wp_page
函數用於處理寫時複製(copy on write
),會在如下兩種狀況處理:
page cache
中,並以只讀模式建立映射,以後發生寫訪問後,觸發COW
;關鍵的複製工做是由wp_page_copy
完成的: