我有一个内核驱动程序,它在内核空间中分配多个缓冲区(物理上连续,与页面边界对齐,并且由整数个页面组成)。接下来,我需要让我的驱动程序能够将其中一些缓冲区映射到用户空间(当然,每个 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() 将失败。