Linux驱动程序:mmap()内核缓冲区到用户空间而不使用nopage

时间:2014-01-14 14:05:13

标签: c linux linux-device-driver

我正在为数据采集设备实现Linux设备驱动程序,该设备不断地将数据流式传输到我在内核中分配的循环缓冲区(使用__get_free_pages())。循环缓冲区(由PCIe硬件写入)驻留在RAM中,我希望用户空间能够mmap()该RAM区域,以便用户空间可以读取其内容。

根据LDD3:

  

remap_pfn_range的一个有趣的限制是它只允许访问物理内存顶部之上的保留页面和物理地址。   ...   因此,remap_pfn_range将不允许您重新映射传统地址,包括您通过调用get_free_page获得的地址。   ...   将真实RAM映射到用户空间的方法是使用vm_ops->nopage一次处理一个页面错误。

在我的情况下,我确切地知道在调用mmap()时,需要将整个缓冲区的地址映射到给定的VMA位置,为什么我必须使用{{1在访问这些页面时,一次一个地出现错误的方法吗?

为什么我不能只设置我的VMA,以便我的整个环形缓冲区立即映射到用户的地址空间?有没有办法做到这一点?

我还期望用户空间程序将按顺序访问我的缓冲区,每次跨越页边界时调用nopage()函数时会导致性能下降。 这是否会在实践中造成相当大的性能损失?(我的缓冲区很大,比如16 MB。)

(值得注意的是,我在以前的一个设备驱动程序中使用nopage()remap_pfn_range()返回的内存中使用__get_free_pages()并且从未遇到过任何问题,但我可能刚刚在该系统上运气不好。)< / p>

1 个答案:

答案 0 :(得分:3)

经过一番研究后,看起来LDD3的陈述已经过时了,按照(略微更新近的)LWN:

TL; DR:过去,驱动程序可能已在PG_reserved / kmalloc()分配的页面上手动设置__get_free_pages(),并随后使用{{1} },但现在驱动程序现在应该使用remap_pfn_range()来执行相同的操作。

vm_insert_page()显然只适用于订单0(单页)分配,因此如果您要分配N个页面,则必须调用vm_insert_page() N次。

可以在Firewire驱动程序中看到此用法的示例:vm_insert_page()

注意如何通过重复调用drivers/firewire/core-iso.c中的alloc_page()来分配单个页面,然后通过重复调用fw_iso_buffer_alloc()中的vm_insert_page()将这些页面映射到用户空间VMA。 (fw_iso_buffer_map_vma()由fw_iso_buffer_map_vma()中的mmap处理程序调用。)