这两个函数都与内存映射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
:需要映射的虚拟内存区域。
star
t:用户空间虚拟地址的起始地址,必须页对齐。
pfn
:物理页帧号的起始地址。
size
:映射的大小(以字节为单位)。
prot
:页面的保护属性(例如PAGE_READONLY
、PAGE_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
是连续内存映射的首选方法,根据具体需求选择合适的工具即可