在 Linux 内核中,我编写了类似
SRC=0x20000000
好的,就在我打完这段话之后,我检查了一下
copy_page_range
(mm/memory.c)的代码,以 COW 优化的方式将内存从一个进程复制到另一个进程。目标和源地址可以通过 PAGE_SIZE
进行偏移,COW 仍然有效。然而,我发现在用户程序中,当我从同一源地址复制到不同的目标地址时,TLB 似乎没有被正确刷新。在高层次上,我的用户级代码执行以下操作(每次精确复制一页,即 0x1000 字节):SRC=0x20000000
- 向 SRC 写入数据(调用相关页
page1
)。 - 调用系统调用将 SRC 复制到目标进程的 0x30000000。现在,源进程地址 0x20000000 和目标进程地址 0x30000000 指向同一页(
page1
)。 - 向 SRC 写入其他内容(这应该会触发页面错误处理 COW)。假设源地址现在指向
page2
。 - 调用系统调用将 SRC 复制到目标进程的 0x30001000。
page2
DST 0x30000000 page1
DST 0x30001000 page2
我发现在第三步时,当我向 src 0x20000000 写入其他内容时,没有生成页面错误。经检查,实际页面映射为:
SRC 0x20000000 page1
DST 0x30000000 page1
DST 0x30001000 page1
在我的代码中,如果我调用 flush_tlb_page
并传递源地址,则用户代码将按预期工作,并具有正确的页面映射。因此,我确信我没有正确地维护 TLB。在 copy_page_range
中,内核在修改页表之前和之后调用 mmu_notifier_invalidate_range_start/end
。我正在做完全相同的事情,并且已经仔细检查了我确实将正确的 struct_mm 和地址传递给 mmu_notifier_invalidate_range_start/end
。这个函数不处理刷新 tlb 吗?好的,就在我打完这段话之后,我检查了一下
dup_mmap
,意识到主要调用者 copy_page_range
的 dup_mmap
(kernel/fork.c)调用了 flush_tlb_mm
。我猜我应该在我的内核代码之前和之后调用 flush_cache_range
和 flush_tlb_range
。这正确吗?mmu_notifier_invalidate_range_start/end
究竟是做什么的?