6

我有一个内核驱动程序,它在内核空间中分配多个缓冲区(物理上连续,与页面边界对齐,并且由整数个页面组成)。接下来,我需要让我的驱动程序能够将其中一些缓冲区映射到用户空间(当然,每个 mmap() 调用一个缓冲区)。驱动程序为此目的注册单个字符设备。用户空间程序必须能够告诉内核它想要映射哪个缓冲区(例如,通过指定其索引或唯一 ID,或先前通过 ioctl() 解析的物理地址)。

我想通过使用 mmap() 的 offset 参数来做到这一点,例如(来自用户空间):

mapped_ptr = mmap(NULL, buf_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (MAGIC + buffer_id) * PAGE_SIZE);

其中“MAGIC”是一些幻数,buffer_id 是我想要映射的缓冲区 ID。接下来,在内核部分会有这样的东西:

static int my_dev_mmap(struct file *filp, struct vm_area_struct *vma)
{
  int bufferID = vma->vm_pgoff - MAGIC;
  /* 
   * Convert bufferID to PFN by looking through driver's buffer descriptors
   * Check length = vma->vm_end - vma->vm_start
   * Call remap_pfn_range()
   */
}

但我认为这是某种肮脏的方式,因为 mmap() 中的“偏移量”不应该指定索引或标识符,它的作用是提供从 mmap-ed 设备开始跳过的字节(或页面)的数量(或文件)内存(应该是连续的,对吧?)。

但是,我已经在主线中看到了一些使用“偏移”来区分 mmap ed 缓冲区的驱动程序。

有没有其他解决方案?

PS 我需要这一切只是因为我正在处理一些不寻常的 SoC 图形控制器,它只能在物理上连续的、与 8 字节边界内存缓冲区对齐的上运行。所以,我只能在内核空间分配这样的缓冲区,并通过 mmap() 将它们传递给用户空间。

控制器的大部分编程(编写指令批处理并将它们推送到内核驱动程序)是在用户空间中执行的。此外,我不能只分配一大块物理上连续的内存,因为在这种情况下它需要非常大(例如,16+ MiB)并且 alloc_pages_exact() 将失败。

4

2 回答 2

1

我认为使用偏移量将索引从用户空间传递给驱动程序没有任何问题。如果它困扰您,那么只需将您的驱动程序视为从它想要呈现给用户空间的各个页面组装一个大缓冲区,因为它实际上是连续的,因此偏移量实际上是该缓冲区的偏移量。但在我看来,以这种方式做事并没有错。

另一种选择,如果您可以使用内核 3.5 或更高版本,可能是使用“连续内存分配器”(CMA)——查看<linux/dma-contiguous.h>drivers/base/dma-contiguous.c了解更多信息。还有https://lwn.net/Articles/486301/作为参考,但我不知道那篇文章和将代码合并到主线之间有多少(如果有的话)变化。

于 2012-09-09T21:12:39.077 回答
1

最后,我选择为每个打开的设备文件描述符(内核中的结构文件)精确映射一个缓冲区,并通过 ioctl() 实现控制:一个 IOCTL 用于分配新缓冲区,一个用于附加到具有已知 ID 的已分配缓冲区,以及另一个获取有关缓冲区的信息。通常,用户空间会同时 mmap() 大约 10..20 个缓冲区,因此对于这种情况,这是一个不错且干净的解决方案。

于 2012-09-10T19:50:15.117 回答