低延迟访问PCI内存BAR(Linux)

时间:2018-07-14 09:22:37

标签: linux memory linux-device-driver mmap pci

背景:

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

目标:

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

可能的前进方式:

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

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

问题:

  1. 在实际读取寄存器时,哪种方法具有最低的延迟?我知道mmap会在内核中造成很多开销,但据我了解,这仅用于初始化。
  2. 方法3是合法的方法吗?对我来说,这似乎是一种hack。如何从应用程序自动确定/ sys /路径?
  3. 方法3和方法4有区别吗?我是PCI驱动程序编程的新手,我认为我并不真正了解4的工作方式。我读过this(以及该书的其他章节),但也许您可以给我一个提示或示例。我会很感激的。

2 个答案:

答案 0 :(得分:1)

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

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

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

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

答案 1 :(得分:1)

添加到@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对该区域进行设置。对于用户空间应用程序,除了使用的设备名称之外,这两种方法之间几乎没有什么区别。对于方法4,驱动程序可能会提供一个象征性的设备名称/dev/clock0或类似的名称,供用户空间应用程序使用(假定该应用程序然后不需要去查找该设备,它只是知道要打开的设备文件名)。

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

Linux驱动程序编程并不十分困难,但是如果您以前没有做过,那会有很大的学习曲线,因此除非确实有必要,否则我绝对不会选择方法4。