![](/img/trans.png)
[英]How does Linux kernel set PCI BARs so that there is no address conflict?
[英]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.