簡體   English   中英

Linux內核如何檢測是否修改了內存地址以實現COW?

[英]How does the Linux kernel detect if a memory address was modified to implement COW?

源代碼在這里:

 #include <stdio.h>
 #include <stdlib.h>

 void main() {
     int *a = malloc(sizeof(int));
     *a = 11;
     int b = 22;//on the stack

     int pid = fork();

     if (pid == 0) {
         printf("pid=%d, a = %d, &a=%p\n", getpid(), *a, a);
         printf("pid=%d, b = %d, &b=%p\n", getpid(), b, &b);
         getchar();
         *a = 33;// ===========cow=========happend here=====
         b = 44;
         printf("pid=%d, a = %d, &a=%p\n", getpid(), *a, a);
         printf("pid=%d, b = %d, &b=%p\n", getpid(), b, &b);
     } else {
         printf("pid=%d, a = %d, &a=%p\n", getpid(), *a, a);
         printf("pid=%d, b = %d, &b=%p\n", getpid(), b, &b);
     }
     pause();
 }

這是將33寫入a的那行的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

在gdb中輸入ni (將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)

內核如何知道它正在更改共享內存,因此需要在寫入之前執行應對措施? 我猜它正在使用類似頁面錯誤中斷的東西。 但是我沒有發現與此有關的任何中斷。

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

我的問題是cpu執行時會發生什么

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

內核如何知道它正在更改共享內存,因此需要在寫入之前執行應對措施? 我猜它正在使用類似頁面錯誤中斷的東西。 但是我沒有發現與此有關的任何中斷。

這是通過處理器和OS的共同努力來實現的。

處理器端:

當CPU執行這樣的指令時:

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

例如,獲取存儲在%rbp寄存器中的地址,並向其添加偏移量-x20,然后對其進行存儲訪問(移動)。

提交內存訪問后,處理器將遍歷硬件頁表(嗯,在大多數情況下,通過訪問TLB是捷徑,但是我在這里只講基本原理)。 當然,頁表應該由操作系統預先設置。

假設處理器進入最終級別的頁面表,只是發現該地址的相應頁面表條目(此答案的其余部分簡稱為pte )表明包含該地址內容的頁面不在內存!(它僅查詢該pte的特定頁面標志),然后根據處理器體系結構引發硬件異常! 根據Intel的術語,它將這種類型的異常歸為錯誤 ,並且您必須經常聽到“頁面錯誤”一詞 (一種可以修復的異常,可以恢復執行,就好像沒有發生此類異常一樣)。根本!)

操作系統方面:

然后,我們將堆棧向上移至OS域。 在引導過程中,操作系統將設置一個異常和中斷處理程序表(在x86術語中,我們稱為IDT ),並將其注冊到處理器。

然后在發生此頁面錯誤時,處理器將執行預設置處理程序(從技術上講,處理器應首先保存CPU上下文,例如推送cs和rip寄存器,rflags寄存器等)。

處理程序可以分為特定於架構的部分(操作系統將進一步執行一些與硬件相關的工作,例如保存更多的寄存器,調用特定於架構的鈎子,確定是否允許頁面錯誤等)和獨立於架構的部分(頁面錯誤邏輯),因此處理程序入口點取決於體系結構也就不足為奇了。

對於x86上的Linux,特定於arch的部分位於arch / x86 / entry / entry_64.S (用於64位)中,而do_page_fault()C函數位於arch / x86 / mm / fault.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()來分配新頁。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM