在 linux 内核中,我编写了类似于copy_page_range
(mm/memory.c) 的代码,因此通过 COW 优化将内存从一个进程复制到另一个进程。目标地址和源地址可以偏移PAGE_SIZE
,但 COW 仍然有效。然而,我注意到,在用户程序中,当我从相同的源地址复制到不同的目标地址时,TLB 似乎没有被正确刷新。在高层次上,我的用户级代码执行以下操作(我一次在我的机器上复制一页,0x1000 字节):
SRC=0x20000000
- 写入 SRC(调用相关页面
page1
)。 - 系统调用将 SRC 复制到目标进程中的 0x30000000。现在,src 进程地址 0x20000000 和目标进程地址 0x30000000 指向同一个页面 (
page1
)。 - 写一些与 SRC 不同的东西(这应该会触发页面错误来处理 COW)。假设源地址现在指向
page2
. - 系统调用将 SRC 复制到目标进程中的 0x30001000。
此时,应该存在两个单独的页面: SRC 0x20000000 page2
DST 0x30000000 page1
DST 0x30001000page2
我发现在第 3 步,当我在 src 0x20000000 中写入不同的内容时,不会产生页面错误。经检查,实际页面映射为:SRC 0x20000000 page1
DST 0x30000000 page1
DST 0x30001000page1
在我的代码中,如果我调用flush_tlb_page
并传递源地址,用户代码将按预期使用正确的页面映射。所以我确信我没有正确维护 TLB。在copy_page_range
中,内核mmu_notifier_invalidate_range_start/end
在更改页表之前和之后调用。我正在做完全相同的事情并仔细检查了我确实将正确的 struct_mm 和地址传递给mmu_notifier_invalidate_range_start/end
. 这个函数不处理刷新 tlb 吗?
好的,从字面上看,当我完成输入时,我检查dup_mmap
并意识到(kernel/fork.c) 的主要调用者调用copy_page_range
了. 我猜我应该在内核代码之前和之后调用。它是否正确?具体是做什么的?dup_mmap
flush_tlb_mm
flush_cache_range
flush_tlb_range
mmu_notifier_invalidate_range_start/end