mmap是一种将文件或设备映射到进程地址空间的内存映射方法,它提供了直接访问设备内存的能力,避免了用户空间和内核空间之间的数据拷贝。
在Linux驱动中实现mmap操作需要提供mmap
文件操作函数:
int (*mmap) (struct file *filp, struct vm_area_struct *vma);
struct vm_area_struct:描述进程的虚拟内存区域,包含以下重要字段:
- vm_start
/vm_end
:虚拟地址范围
- vm_page_prot
:保护权限
- vm_flags
:区域标志
- vm_ops
:操作集合指针
static int mydrv_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct my_device *dev = filp->private_data;
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
unsigned long size = vma->vm_end - vma->vm_start;
// 检查映射范围是否合法
if (offset + size > dev->mem_size)
return -EINVAL;
// 设置内存区域不可缓存
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
// 映射物理内存到用户空间
if (remap_pfn_range(vma, vma->vm_start,
dev->phys_addr >> PAGE_SHIFT,
size, vma->vm_page_prot))
return -EAGAIN;
return 0;
}
static const struct vm_operations_struct mydrv_vm_ops = {
.open = mydrv_vma_open,
.close = mydrv_vma_close,
.fault = mydrv_vma_fault,
};
static int mydrv_mmap(struct file *filp, struct vm_area_struct *vma)
{
// ... 基本检查 ...
vma->vm_ops = &mydrv_vm_ops;
mydrv_vma_open(vma);
return 0;
}
当访问未映射的页面时触发页错误处理:
static int mydrv_vma_fault(struct vm_fault *vmf)
{
struct page *page;
unsigned long offset;
struct my_device *dev = vmf->vma->vm_private_data;
offset = vmf->pgoff - (vmf->vma->vm_pgoff);
if (offset >= dev->num_pages)
return VM_FAULT_SIGBUS;
page = virt_to_page(dev->mem_base + (offset << PAGE_SHIFT));
get_page(page);
vmf->page = page;
return 0;
}
根据设备特性选择合适的映射方式:
// 无缓存映射(适合寄存器访问)
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
// 写合并映射
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
// 默认缓存映射(适合普通内存)
// 无需修改vm_page_prot
int main()
{
int fd = open("/dev/mydrv", O_RDWR);
void *mapped = mmap(NULL, MAP_SIZE, PROT_READ|PROT_WRITE,
MAP_SHARED, fd, 0);
if (mapped == MAP_FAILED) {
perror("mmap");
return -1;
}
// 直接访问设备内存
*((volatile uint32_t *)mapped) = 0x12345678;
uint32_t val = *((volatile uint32_t *)mapped);
munmap(mapped, MAP_SIZE);
close(fd);
return 0;
}
/proc/<pid>/maps
查看映射是否正确建立strace
跟踪mmap系统调用perf
或ftrace
分析性能问题mmap操作是Linux驱动开发中强大的工具,合理使用可以显著提高I/O性能,但也需要谨慎处理安全和稳定性问题。