用户态内存映射(mmap)

将驱动中分配的单个页面映射到用户空间

1. 概述

mmap(内存映射)是一种强大的机制,允许用户空间程序直接访问内核空间的内存。通过mmap,我们可以将驱动中分配的单个页面映射到用户空间,从而实现高效的数据共享和零拷贝操作。

2. mmap的基本原理

mmap系统调用将设备内存或文件映射到进程的地址空间,使得应用程序可以像访问普通内存一样访问设备内存或文件内容。

内核空间 驱动分配的内存页 用户空间 进程地址空间 mmap映射

3. 驱动中的mmap实现

在Linux内核驱动中,实现mmap功能主要涉及以下步骤:

  1. 实现file_operations结构体中的mmap函数指针
  2. 使用remap_pfn_range或io_remap_pfn_range函数建立映射
  3. 处理必要的权限和缓存设置

3.1 关键数据结构

数据结构 描述
struct vm_area_struct 描述进程虚拟地址空间的一个区域
struct file_operations 包含文件操作函数指针的结构体

3.2 示例代码

以下是一个简单的mmap驱动实现示例:

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/slab.h>

static void *kernel_buffer;
static struct page *mmap_page;

static int my_mmap(struct file *filp, struct vm_area_struct *vma)
{
    unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
    unsigned long phys = virt_to_phys(kernel_buffer);
    unsigned long vsize = vma->vm_end - vma->vm_start;
    unsigned long psize = PAGE_SIZE - offset;
    
    if (vsize > psize)
        return -EINVAL;
    
    vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
    
    if (remap_pfn_range(vma, vma->vm_start, 
                        phys >> PAGE_SHIFT, 
                        vsize, vma->vm_page_prot))
        return -EAGAIN;
    
    return 0;
}

static struct file_operations fops = {
    .mmap = my_mmap,
};

static int __init my_init(void)
{
    kernel_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
    if (!kernel_buffer)
        return -ENOMEM;
    
    mmap_page = virt_to_page(kernel_buffer);
    
    // 注册设备等操作...
    return 0;
}

static void __exit my_exit(void)
{
    kfree(kernel_buffer);
    // 注销设备等操作...
}

module_init(my_init);
module_exit(my_exit);

4. 用户空间的使用

在用户空间程序中,使用mmap系统调用将驱动内存映射到进程地址空间:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>

int main() {
    int fd = open("/dev/mydevice", O_RDWR);
    if (fd < 0) {
        perror("open");
        return -1;
    }
    
    void *mapped = mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE, 
                       MAP_SHARED, fd, 0);
    if (mapped == MAP_FAILED) {
        perror("mmap");
        close(fd);
        return -1;
    }
    
    // 现在可以直接访问映射的内存
    printf("Mapped memory content: %s\n", (char*)mapped);
    
    // 写入数据
    sprintf((char*)mapped, "Hello from userspace!");
    
    munmap(mapped, PAGE_SIZE);
    close(fd);
    return 0;
}

5. 注意事项

6. 总结

mmap是一种高效的内存共享机制,通过将内核空间的内存映射到用户空间,可以实现零拷贝的数据传输和直接内存访问。正确使用mmap可以显著提高应用程序的性能和效率。