如何从 Linux 设备驱动程序强制刷新进程的缓存?

How can I force a cache flush for a process from a Linux device driver?

我正在从事一项研究项目,该项目要求我从自定义硬件执行内存捕获。我正在使用 Zedboard SoC(带有 FPGA 结构的双核 ARM Cortex-A9)。我设计了一个设备驱动程序,允许我执行虚拟内存捕获和物理内存捕获(使用控制 Xilinx AXI DMA IP 的 AXI4-Lite 外设)。

我的目标是捕获所有映射页面,所以我检查 /proc/pid/maps 映射区域,然后从 /proc/pid/pagemaps 获取 PFN,将物理地址传递到我的设备驱动程序,然后将它们传递给我的自定义硬件(它调用 Xilinx AXI DMA 从物理内存中获取内容)。

注意:我使用的是 Xilinx 的 PetaLinux 发行版,它基于 Linux 4.14 版构建。

我的设备驱动程序通过一系列 IOCTL 调用实现了以下过程:

  1. 停止目标进程。
  2. 执行虚拟内存捕获(使用access_process_vm()函数)。
  3. 刷新缓存(使用flush_user_range()函数)。
  4. 执行物理内存捕获。
  5. 恢复目标进程。

然而,我注意到的是虚拟内存捕获和物理内存捕获在 [heap] 部分(这是超出一页的第一部分)中有所不同。第一页匹配,但其他页面的 none 更接近。 [stack] 部分根本不匹配。我应该注意到,对于前两个内存部分,.text 和 .rodata,捕获完全匹配。目前的结论是, 不会 在运行时更改 的数据在虚拟捕获和物理捕获之间匹配,而 [=29 的数据=]does change during runtime does not match.

所以这让我想知道:我是否使用了正确的函数来确保缓存和 RAM 之间的一致性?如果不是,用于强制缓存刷新到 RAM 的正确函数是什么? RAM 中的数据有必要在目标进程停止时保持最新状态,因为我无法从自定义硬件访问缓存。

编辑 1: 关于这个问题被标记为 this question 的可能重复项,我正在使用已接受答案中的函数来启动缓存刷新。但是,从我的角度来看,它似乎没有工作,因为物理内存与虚拟内存不匹配,正如我所期望的那样,如果发生缓存刷新。

对于以后遇到这个问题的人来说,问题不是我想的那样。我提到的 flush_user_range() 函数是用于将页面从缓存推回主内存的正确函数。

然而,我当时没有想到的是 实际上 连续的页面 不一定(并且是通常不是)物理上 连续。在我的内核代码中,我将映射区域的长度传递给我的硬件,硬件从 AXI DMA 请求该长度的数据。我应该做的是虚拟到物理的转换,以获取每个区域中每个页面的物理地址,然后从主内存请求一个页面长度的数据,对每个映射区域中的每个页面重复该过程。

我意识到这是一个非常具体的问题,可能不会帮助其他人做我正在做的同样的事情,但希望吸取的教训可以帮助将来的人:Linux 在物理中分配页面内存(通常大小为 4kB,但您不应该假设是这种情况),并且物理页面的集合包含在一个映射区域中。如果您正在处理任何需要检查物理内存的代码,请务必警惕数据可能跨越物理页面边界的位置并采取相应措施。