vmf_insert_pfn 和 remap_pfn_range 的功能与区别

2024-11-30 182 0

这两个函数都与内存映射mmap相关,主要用于将内核中的内存映射到用户空间。它们的功能略有不同,适用的场景也不尽相同。

vmf_insert_pfn

vmf_insert_pfn 是用于向用户空间 vm_area_struct(VMA)中插入一个单独的页表条目。它适合细粒度控制单个页的映射。

用法

int vmf_insert_pfn(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn);

参数说明:

vma:表示需要插入页的虚拟内存区域。 addr:用户空间虚拟地址,页对齐。 pfn:物理页帧号(Page Frame Number)。

功能:

将给定的物理页帧插入到用户空间地址 addr 所对应的页表中。 用于非连续内存或动态页插入的场景。

返回值:

返回 0 表示成功。 返回负值表示失败,例如地址不对齐或 VMA 无效。

使用场景

动态插入页: 例如,在设备驱动程序中,需要按需向用户进程提供一部分物理内存。 零散的页映射: 当映射的物理内存不是连续的,可以使用 vmf_insert_pfn 一页一页地映射。

示例代码

static int mmap_fault(struct vm_fault *vmf) {
    unsigned long pfn = virt_to_phys(kernel_buffer) >> PAGE_SHIFT;

    // 向用户空间插入页
    int ret = vmf_insert_pfn(vmf->vma, vmf->address, pfn);
    if (ret) {
        printk(KERN_ERR "vmf_insert_pfn failed: %d\n", ret);
        return VM_FAULT_SIGBUS;
    }

    return VM_FAULT_NOPAGE;
}

remap_pfn_range

remap_pfn_range 用于将一段连续的物理页映射到用户空间虚拟地址范围内。

用法

int remap_pfn_range(struct vm_area_struct *vma,
                    unsigned long start,
                    unsigned long pfn,
                    unsigned long size,
                    pgprot_t prot);

参数说明:

vma:需要映射的虚拟内存区域。 start:用户空间虚拟地址的起始地址,必须页对齐。 pfn:物理页帧号的起始地址。 size:映射的大小(以字节为单位)。 prot:页面的保护属性(例如PAGE_READONLYPAGE_SHARED)。

功能:

将一段连续的物理地址映射到用户空间。 pfn 对应的物理内存需要是连续的。

返回值:

返回 0 表示成功。 返回负值表示失败,例如参数无效。

使用场景

连续内存映射: 用于将设备的寄存器地址或 DMA 缓冲区等连续物理内存映射到用户空间。 内存分区共享: 用于共享一段连续的内存区域给用户空间。

示例代码

static int mmap_mmap(struct file *file, struct vm_area_struct *vma) {
    unsigned long pfn = virt_to_phys(kernel_buffer) >> PAGE_SHIFT;
    unsigned long size = vma->vm_end - vma->vm_start;

    // 检查映射范围是否有效
    if (size > BUFFER_SIZE) {
        printk(KERN_ERR "mmap_device: Requested size exceeds buffer\n");
        return -EINVAL;
    }

    // 将物理内存映射到用户空间
    if (remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot)) {
        printk(KERN_ERR "mmap_device: remap_pfn_range failed\n");
        return -EAGAIN;
    }

    printk(KERN_INFO "mmap_device: mmap successful\n");
    return 0;
}

两者的主要区别

特性 vmf_insert_pfn remap_pfn_range
用途 单个页帧插入 映射一段连续的物理页
物理内存连续性要求 无需连续 需要连续
典型场景 动态按需插入页帧,适合分散的页 映射设备内存或共享一段连续的内存区域
效率 一页一页插入,较慢 一次性映射整段内存,较快
实现粒度 细粒度 粗粒度
对 fault 处理支持 通常在 page fault 时调用 通常在 mmap 初始化阶段调用

选择指南

连续内存:

如果你需要映射一段连续的物理内存,优先选择 remap_pfn_range。 它更适合映射设备内存或预先分配好的 DMA 缓冲区。

非连续内存:

如果内存是非连续的,或者映射是按需动态发生的(例如 fault 时处理),使用 vmf_insert_pfn

性能:

对于一次性大范围映射,remap_pfn_range 是更高效的选择。
如果需要动态映射单个页,vmf_insert_pfn 提供了更细粒度的控制。

两者结合使用

在某些复杂场景下,可以结合两者使用。例如:

初始化阶段用 remap_pfn_range 映射一段连续内存。 运行时如果用户空间需要额外页,可以用 vmf_insert_pfn 插入新的页。 总结:vmf_insert_pfn 适合单页的动态插入场景,而 remap_pfn_range 是连续内存映射的首选方法,根据具体需求选择合适的工具即可

相关文章

发布评论