2016-06-01 67 views
2

的源代碼在這裏:Linux內核如何檢測內存地址是否被修改以實現COW?

​​

這裏是一個線,寫到一個, 我設置一個斷點這裏的GDB拆機。並開始這個程序。然後使用崩潰看到物理地址的

>│0x40073a <main+154>  movl $0x2c,-0x20(%rbp) //copy on write happend here                                │ 
    │0x400741 <main+161>  mov -0x18(%rbp),%rax                                 │ 
    │0x400745 <main+165>  mov (%rax),%ebx 

線性地址0x602010,所以使用VTOP,我得到這個:

我們可以看到他們指向相同的物理地址2a683010

PID: 6468 
COMMAND: "a.out" 
    TASK: ffff88007c317300 [THREAD_INFO: ffff880016728000] 
    CPU: 0 
    STATE: TASK_TRACED|TASK_WAKEKILL 
crash> vtop 0x602010 
VIRTUAL  PHYSICAL 
602010  2a683010 

    PML: 24e6f000 => 2409c067 
    PUD: 2409c000 => 7144067 
    PMD: 7144018 => 19847067 
    PTE: 19847010 => 800000002a683065 
    PAGE: 2a683000 



    PID: 6464 
COMMAND: "a.out" 
    TASK: ffff880036992280 [THREAD_INFO: ffff880014e38000] 
    CPU: 0 
    STATE: TASK_TRACED|TASK_WAKEKILL 
crash> vtop 0x602010 
VIRTUAL  PHYSICAL 
602010  2a683010 

    PML: 36df9000 => 3a654067 
    PUD: 3a654000 => 1a71a067 
    PMD: 1a71a018 => 18f2a067 
    PTE: 18f2a010 => 800000002a683065 
    PAGE: 2a683000 

在類型ni in gdb(它將a的值更改爲33)後,再次使用vtop。我可以看到進程的一個物理地址已經改變。

crash> vtop 0x602010 
VIRTUAL  PHYSICAL 
602010  5d755010 

    PML: 24e6f000 => 2409c067 
    PUD: 2409c000 => 7144067 
    PMD: 7144018 => 19847067 
    PTE: 19847010 => 800000005d755067 
    PAGE: 5d755000 



crash> vtop 0x602010 
VIRTUAL  PHYSICAL 
602010  2a683010 

    PML: 36df9000 => 3a654067 
    PUD: 3a654000 => 1a71a067 
    PMD: 1a71a018 => 18f2a067 
    PTE: 18f2a010 => 800000002a683065 
    PAGE: 2a683000 

我的問題是,當CPU執行

movl $0x2c,-0x20(%rbp) 

內核怎麼知道它正在改變共享內存,所以需要應對在寫之前進行發生了什麼?我猜測它正在使用諸如頁面錯誤中斷之類的東西。但是我沒有發現任何與此相關的中斷。

如果內核負責這個,請提供內核的源代碼。

+1

*請提供內核的源代碼,如果內核負責這個。* https://github.com/torvalds/linux –

+1

@AndrewHenle不錯的一個:-) –

+0

我覺得這個更好,然後github https ://code.woboq.org/linux/linux/ –

回答

4

我的問題是,當CPU執行

MOVL $ 0x2c上,-0x20(%RBP)

怎麼內核知道它是不斷變化的共享內存,因此需要一個 應對發生了什麼在寫之前執行?我猜測它正在使用諸如頁面錯誤中斷之類的東西。但是我沒有發現任何與此相關的中斷。

這是在處理器和操作系統的協同努力下實現的。

處理器側:

當CPU執行這樣的指令:

MOVL $ 0x2c上,-0x20(%RBP)

即讀取存儲在地址%rbp寄存器並向它添加一個偏移量-x20,然後發出對它的存儲器訪問(movl)。

在提交內存訪問時,處理器將遍歷硬件頁表(當然,在大多數情況下,通過訪問TLB可以簡化操作,但我只是在此討論基本原理)。當然,頁表應該事先由OS設置。

假設處理器使得它的方式到最後一級頁表,只是認定相應的頁表項(將其稱之爲PTE簡稱爲這個答案的其餘部分)正是由於這個解決 提示頁面包含地址內容不在內存中!(它只是查閱該pte的特定頁面標誌),然後根據處理器體系結構,引發硬件異常!根據英特爾術語,它將此類異常分類爲故障,並且您必須經常聽到術語的'頁面錯誤'(一種可以修復的異常,並且可以恢復執行,好像沒有這樣的異常,在所有發生過)

操作系統方面:

然後我們棧向上移動到操作系統領域。在引導過程中,操作系統將設置一個異常和中斷處理程序表(在x86行話中我們稱之爲IDT),並將其註冊到處理器。

然後在這個頁面發生故障時,預設置處理程序由處理器執行(技術上,處理器應首先保存CPU上下文,例如按cs和rip寄存器,rflags寄存器等)。

處理程序可以分爲特定於arch的部分(操作系統將進一步執行一些與硬件相關的作業,例如保存更多寄存器,調用特定於arch的鉤子,確定頁面錯誤是否允許?等)以及獨立於arch的部分(頁面錯誤邏輯),所以處理程序入口點依賴於arch是毫不奇怪的。在x86

對於Linux,特定拱形部分位於拱/ 86 /進入/ entry_64.S(64位)和在拱/ 86 /毫米/故障的do_page_fault()C函數。 c。然後在do_page_fault()中,它將調用與拱無關的C函數handle_mm_fault(),該函數位於核心MM代碼mm/memory.c

對於這個問題,在handle_mm_fault()中,do_wp_page()處理COW邏輯。基本上,handle_mm_fault()只是遍歷錯誤地址的頁表,並發現它是一個寫保護頁面(存在,但寫標誌未設置),所以它調用do_wp_page()來分配一個新頁面。