date: 2014-09-27 19:09app
注:本文展現的代碼來自2.4.0版本的內核,入口函數do_page_fault定義在<arch/mm/fault.c>中。函數
整體處理流程:指針
備註:code
- do_page_fault前半部分流程請參考「越界訪問」的情景分析。
- 虛存區間結構vm_area中包含一個vm_operations_struct類型的指針vm_ops。vm_operations_struct定義了一組函數指針,其中的nopage函數指針指定了當該區間發生了頁面異常時應該執行的操做,詳情請參考《虛存管理中的抽象》。做爲堆棧區間的vma,跟文件系統或頁面共享沒有關係,於是沒有指定nopage函數,內核將調用do_anonymous_page函數。
- 「讀操做觸發的頁面異常」,開始時都一概映射到同一個物理內存頁面empty_zero_page上,而無論其虛擬地址是什麼。empty_zero_page的定義以下:
<include/asm/Pgtable.h>
/*
* ZERO_PAGE is a global shared page that is always zero: used
* for zero-mapped memory areas etc..
*/
extern unsigned long empty_zero_page[1024];
#define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page))
可見這個頁面的內容全爲0,也就是說,在映射之初去讀的話讀出的值全爲0。這是能夠理解的:你去讀一個「還沒有創建映射」的地址,這個地址對用戶空間來講,就像是一塊新的未開墾(未進行寫操做)的土地,讀出的結果爲0是理所應當的。若是後續要對該地址進行寫操做,那麼內核將會爲該虛擬地址從新映射到一塊新的的物理頁面上。這就是內核中的COW(Copy On Write)技術,在《進程調度與切換》章節中咱們還會看到。blog
- 整個堆棧擴展的過程能夠總結爲:分配新的物理頁面,經過設置頁面表將虛擬地址映射到物理頁面上。
- 異常返回到用戶空間後,將從新執行因異常而「夭折」的指令,對於該場景就是從新執行「子函數返回地址入棧」的指令,而後再繼續往下執行,這點與中斷不一樣。中斷髮生時,CPU會將下一條指令也就是接下來本該執行的指令的地址壓入堆棧,做爲中斷服務的返回地址;而異常發生時,CPU將因異常沒法完成的指令(也就是觸發異常的指令)自己的地址壓入堆棧,做爲異常服務程序的返回地址,這樣從異常服務返回時,就能夠「從頭再來」完成未竟的事業。這種差別性(返回地址的差別),是CPU內部的硬件電路實現的,不須要軟件的干預。