以低延迟访问 PCI 内存条 (Linux)

Posted

技术标签:

【中文标题】以低延迟访问 PCI 内存条 (Linux)【英文标题】:Access PCI memory BAR with low latency (Linux) 【发布时间】:2018-07-14 09:22:36 【问题描述】:

背景:

我有一个 PCI 卡,它基本上是一个时钟。它通过 GPS 获取时间并将当前时间保存在某个寄存器中。

目标:

我想以尽可能低的延迟一遍又一遍地读取有限数量的寄存器/字节(例如当前时间)。 (时钟提供了非常高的精度,我认为延迟越高,精度就越低。)。操作系统是红帽。编程语言是 C/C++。我还想写入设备内存,因此延迟不是问题。

可能的方法:

我看到了这些方式。如果你看到另一个,请告诉我:

    编写 Linux 内核模块驱动程序,该驱动程序创建一个字符设备(或一个字符设备供每个寄存器读取)。然后用户空间应用程序可以对 /dev/ 文件进行“读取”。 DMA 通过用户空间应用程序(系统调用)将 sysfs resourceX 文件映射到用户空间。 (例如here) 编写实现 mmap 文件操作的 Linux 内核模块驱动程序。

问题:

    在实际读取寄存器时,哪种方式延迟最低?我知道 mmap 会在内核中造成大量开销,但据我所知,这仅用于初始化。 方式 3 是否合法?对我来说,这看起来像是一个黑客。如何从应用程序中自动确定 /sys/ 路径? 方式 3 和方式 4 有区别吗?我是 PCI 驱动程序编程的新手,我想我并不真正了解 4 的工作方式。我读过this(以及那本书的其他章节),但也许你可以给我一个提示或一个例子。我将不胜感激。

【问题讨论】:

在Linux中有一种VDSO机制,其目的之一是提供gettimeofday()的最快方式。如果您的时钟驱动程序可以更新 VDSO 中的变量,那将是有史以来最快的方式。 【参考方案1】:

方法 3 或 4 应该可以正常工作。它们在延迟方面没有区别。延迟大约为 100 ns。

如果您需要初始化设备,或控制允许哪些应用程序访问它,或一次强制使用一个读取器等,则需要方法 4。方法 3 确实有点像 hack,因为它跳过了所有这个的。但是如果你不需要这些东西就更简单了。

字符设备的延迟肯定更高,因为每次读取设备时都需要内核转换。

DMA 方法的延迟完全取决于设备将时间写入内存的频率。 CPU 访问内存的延迟比 MMIO 低,但如果设备每毫秒只执行一次 DMA,那么这就是您的延迟。此外,该方法会产生大量无用的 DMA 流量,因为 CPU 读取值的频率远低于写入值。

【讨论】:

我没有解决问题 2 的第二部分。也许其他人可以提供帮助。 感谢您的回答。您的最后一句话:“此外,该方法会产生大量无用的 DMA 流量,因为 CPU 读取值的频率远低于写入值。”只是为了确保我理解正确:流量是由设备的写入引起的,其中大部分是无用的,因为 CPU 只读取这些写入的一部分。正确的?您是否有提示或示例如何实施方式 4?我没有找到任何可以帮助我理解的东西。 我将从您链接的同一本书开始;这是我过去使用的。【参考方案2】:

添加到@prl 的答案...

方法 3 对我来说似乎完全合法。这就是它的用途。你可能想看看内核文档文件:https://www.kernel.org/doc/Documentation/filesystems/sysfs-pci.txt

您还可以使用/sys 文件系统来查找您的设备。首先,记下时钟卡的供应商 ID 和设备 ID(以及子系统供应商/设备,如有必要),然后您可以轻松遍历 /sys/devices 层次结构,寻找匹配的设备(使用 vendordevice、等特殊文件)。找到它后,您大概知道要从设备的数据表中打开哪个 resourceN 文件,然后在适当的偏移处打开 mmap 文件,您就完成了。

所有这些都假定您的设备已经配置并启用。通常,当系统启动时,PCI 设备无法执行任何操作。一些驱动程序需要声明设备,并初始化/配置它。完成此操作后,如果仅通过读取一两个寄存器即可访问时间,则可以使用方法 3。(我不确定:PCI 设备 可能自初始化,但我从未见过。我认为可能至少需要启用其内存空间。如果设置足够小/足够简单,可能可以从用户空间完成。)

与方法 4 的主要区别在于,控制设备的驱动程序将支持允许该区域被明确地mmap'd。对于用户空间应用程序,除了使用的设备名称外,这两种方法几乎没有区别。对于方法 4,驱动程序可能会提供一个符号设备名称 /dev/clock0 或类似的名称供用户空间应用程序使用(并且可能应用程序不需要去查找设备,它只需 知道要打开的设备文件名)。

在用户空间中,您将以与任一方法大致相同的方式执行mmap 操作。在方法 4 中,驱动程序在内部提供要映射的物理地址——可能还有偏移量——而不是通用 PCI 子系统这样做,但无论哪种方式,它都只是 open + mmap

Linux 驱动程序编程并不是非常困难,但是如果你以前没有做过的话,那里的学习曲线很长,所以我绝对不会使用方法 4,除非真的需要这样做。

【讨论】:

以上是关于以低延迟访问 PCI 内存条 (Linux)的主要内容,如果未能解决你的问题,请参考以下文章

哪个 iOS 音频 API 设计用于以低延迟从磁盘流式传输大型本地音频文件?

Notes17内核中断,通过IO内存访问外设,pci

深入理解JVM(③)ZGC收集器

Linux内存管理

ZGC的并发映射并不是以一个必须要“迫切”去完成的任务

心得体会