7

在 linux 内核中,我编写了类似于copy_page_range(mm/memory.c) 的代码,因此通过 COW 优化将内存从一个进程复制到另一个进程。目标地址和源地址可以偏移PAGE_SIZE,但 COW 仍然有效。然而,我注意到,在用户程序中,当我从相同的源地址复制到不同的目标地址时,TLB 似乎没有被正确刷新。在高层次上,我的用户级代码执行以下操作(我一次在我的机器上复制一页,0x1000 字节):

SRC=0x20000000

  1. 写入 SRC(调用相关页面page1)。
  2. 系统调用将 SRC 复制到目标进程中的 0x30000000。现在,src 进程地址 0x20000000 和目标进程地址 0x30000000 指向同一个页面 ( page1)。
  3. 写一些与 SRC 不同的东西(这应该会触发页面错误来处理 COW)。假设源地址现在指向page2.
  4. 系统调用将 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_mmapflush_tlb_mmflush_cache_rangeflush_tlb_rangemmu_notifier_invalidate_range_start/end

4

1 回答 1

9

是的,如果您正在执行更改页表的操作,则需要确保 TLB 按要求无效。

mmu_notifier_invalidate_range_start/end只是调用 MMU 通知程序挂钩;这些钩子仅存在以便当 TLB 失效发生时可以告知其他内核代码。唯一设置 MMU 通知器的地方是

  • KVM(硬件辅助虚拟化)使用它们来处理换出页面;它需要了解主机 TLB 失效以使虚拟化来宾 MMU 与主机保持同步。
  • GRU(大型 SGI 系统中专用硬件的驱动程序)使用 MMU 通知器来保持 GRU 硬件中的映射表与 CPU MMU 同步。

但几乎任何你调用 MMU 通知钩子的地方,如果内核还没有为你做,你也应该调用 TLB 击落函数。

于 2011-12-05T07:14:04.710 回答