Linux内核如何检测内存地址是否被修改以实现COW?
源代码如下:Linux内核如何检测内存地址是否被修改以实现COW?,c,linux,linux-kernel,copy-on-write,C,Linux,Linux Kernel,Copy On Write,源代码如下: #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", g
#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();
}
在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)
内核如何知道它正在更改共享内存,因此在编写之前需要执行一个处理?我猜它使用了类似于页面错误中断的东西。但我没有发现任何与此相关的中断
这是通过处理器和操作系统的协作实现的
处理器端:
当CPU执行此类指令时:
movl$0x2c,-0x20(%rbp)
i、 e.获取存储在%rbp寄存器中的地址,并向其添加偏移量-x20,然后对其发出内存访问(movl)
在提交内存访问时,处理器将遍历硬件页表(在大多数情况下,访问TLB是一条捷径,但我在这里只讨论基本原则)。当然,页面表应该由操作系统预先设置
假设处理器到达最后一级页表,只找到该地址对应的页表条目(将其称为pte)
建议包含地址内容的页面不在内存中!(它只参考该pte的特定页面标志),然后,根据处理器体系结构,引发硬件异常!根据Intel术语,它将此类异常分类为故障,您必须经常听到术语“页面故障”(一种可以修复并恢复执行的异常,就好像根本没有发生此类异常一样!)
操作系统端:
然后我们将堆栈向上移动到操作系统域中。在引导过程中,操作系统将设置一个异常和中断处理程序表(用x86术语我们称之为IDT),并将其注册到处理器
然后,一旦发生此页面错误,处理器将执行预设置处理程序(从技术上讲,处理器应首先保存CPU上下文,如推动cs和rip寄存器、rflags寄存器等)
处理程序可以分为特定于arch的部分(操作系统将进一步执行一些与硬件相关的工作,如保存更多寄存器、调用特定于arch的钩子、确定是否允许页面错误等)和独立于arch的部分(页面错误逻辑),因此毫不奇怪,处理程序入口点依赖于arch
对于x86上的Linux,特定于arch的部分位于arch/x86/entry/entry_64.S(对于64位)和arch/x86/mm/fault.C中的do_page_fault()C函数中。然后在do_page_fault()中,它将调用arch独立的C函数handle_mm_fault(),该函数位于核心mm代码mm/memory.C
对于这个问题,在handle_mm_fault()中,do_wp_page()处理COW逻辑。基本上,handle_mm_fault()只是遍历出错地址的页表,发现它是一个写保护页(存在,但未设置写标志),因此它调用do_wp_page()来分配一个新页。如果内核负责此操作,请提供内核的源代码@AndrewHenle不错的一个:-)我觉得这一个比github好。好了,这一个怎么样:
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
movl $0x2c,-0x20(%rbp)