在linux内核中,我编写了类似于copy_page_range
(mm / memory.c)的代码,因此通过COW优化将内存从一个进程复制到另一个进程。目标地址和源地址可以PAGE_SIZE
偏移,COW仍可正常工作。但是,我注意到,在用户程序中,当我从相同的源地址复制到不同的目标地址时,TLB似乎没有被正确刷新。在较高级别,我的用户级代码执行以下操作(我一次只复制一页,我的机器上的0x1000字节):
SRC = 0x20000000
page1
)。page1
)。page2
。此时,应该存在两个单独的页面:
SRC 0x20000000 page2
DST 0x30000000 page1
DST 0x30001000 page2
我发现在第3步,当我在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)的主调用者调用了{{1} }}。我猜我应该在我的内核代码之前和之后调用flush_tlb_mm
和flush_cache_range
。它是否正确? flush_tlb_range
究竟做了什么?
答案 0 :(得分:9)
是的,如果你正在做一些改变页面表的事情,你需要确保TLB根据需要失效。
mmu_notifier_invalidate_range_start/end
只是调用MMU通知程序挂钩;这些挂钩只存在,以便在发生TLB失效时可以告知其他内核代码。设置MMU通知程序的唯一地方是
但几乎任何你调用MMU通知程序挂钩的地方,如果内核还没有为你做这个,你也应该调用TLB击落函数。