remap_pfn_range、vm_pgoff、pfn

http://linux.chinaunix.net/bbs/archiver/tid-900802-page-2.html
http://www.linuxforum.net/forum/showflat.php?Cat=&Board=linuxK&Number=651015&page=0&view=collapsed&sb=5&o=7&fpart=

1. vma->vm_pgoff 是 该vm 区域在map file中的以PAGE大小为单位的偏移。/* Offset (within vm_file) in PAGE_SIZE*/

比如4个vm_area MAP到一个文件的
1)0-----4096
2)5*4096 ------ 7*4096
3)9*4096 ------ 10*4096
4)15*4096 ----- 20*4096

那么4个vma的vm_pgoff分别为0,5,9, 15

本来和物理页号没有关系。

你们看到的和物理页号关系是一种特例和巧合

2, remap_pfn_range 函数是被驱动调用(cd drivers     grep -r remap_pfn * )
用来将"所有"物理内存影射成一个文件中(driver/mem.c 参见)。

这时候,vm_pgoff就和物理PAGE号有关系了。是特例和巧合。
vm_pgoff的本意和物理PAGE号无关。
就是我上贴说的定义(看mm.h 一句话的说明: Offset (within vm_file) in PAGE_SIZE)

3. drivers/char/mem.c
   static int mmap_mem(struct file *file, struct vm_area_struct *vma)
{
    size_t size = vma->vm_end - vma->vm_start;

    if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size))
        return -EINVAL;

    if (!private_mapping_ok(vma))
        return -ENOSYS;

    if (!range_is_allowed(vma->vm_pgoff, size))
        return -EPERM;

    if (!phys_mem_access_prot_allowed(file, vma->vm_pgoff, size,
                        &vma->vm_page_prot))
        return -EINVAL;

    vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff,
                         size,
                         vma->vm_page_prot);

    vma->vm_ops = &mmap_mem_ops;

    /* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */
    if (remap_pfn_range(vma,
                vma->vm_start,
                vma->vm_pgoff,
                size,
                vma->vm_page_prot)) {
        return -EAGAIN;

    }
    return 0;
}

#ifdef CONFIG_DEVKMEM
static int mmap_kmem(struct file *file, struct vm_area_struct *vma)
{
    unsigned long pfn;

    /* Turn a kernel-virtual address into a physical page frame */
    pfn = __pa((u64)vma->vm_pgoff << PAGE_SHIFT) >> PAGE_SHIFT;

    /*
     * RED-PEN: on some architectures there is more mapped memory than
     * available in mem_map which pfn_valid checks for. Perhaps should add a
     * new macro here.
     *
     * RED-PEN: vmalloc is not supported right now.
     */
    if (!pfn_valid(pfn))
        return -EIO;

    vma->vm_pgoff = pfn;
    return mmap_mem(file, vma);

}
### mmap底层驱动实现原理 #### 驱动层的mmap接口 在Linux内核中,`mmap()`系统调用允许用户空间程序映射文件或设备到内存地址空间。对于设备而言,这通常通过特定于设备的驱动程序来处理。以Binder驱动为例,在`/drivers/staging/android/binder.c`中有如下定义: ```c static int binder_mmap(struct file *filp, struct vm_area_struct *vma) { ... } ``` 此函数负责设置虚拟内存区域结构(`vm_area_struct`)并初始化必要的硬件资源以便后续访问[^1]。 #### 内存映射的具体过程 当应用程序请求将一段物理内存映射至其进程上下文中时,操作系统会执行一系列操作确保安全性和效率: - **权限验证**: 检查当前线程是否有权访问指定范围内的页面。 - **创建VMA(虚拟内存区)**: 如果成功,则建立一个新的VMA节点表示新分配的空间;否则返回错误码给用户态应用。 - **填充页表项(PTEs)**: 对应于所申请区间内的每一个逻辑地址都需配置好相应的PTE指向实际RAM中的位置。 特别需要注意的是,在某些情况下(比如帧缓冲器),还需要考虑目标对象尺寸与请求长度的关系——即不能超出也不可不足,以免造成越界读写或是显示不全等问题[^2]。 #### 实现细节展示 下面给出简化版伪代码用于说明上述机制的工作流程: ```c // 假设这是某个字符型设备对应的mmap方法实现 int example_device_mmap(struct file *file_ptr, struct vm_area_struct *area){ // 获取设备私有数据指针 struct device_data* dev = (struct device_data*)file_ptr->private_data; // 设置保护标志位以及共享属性等参数... area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; // 调用remap_pfn_range完成最终映射动作 unsigned long offset_pages = area->vm_pgoff; // 计算偏移量转换成页数形式 if (!remap_pfn_range(area, area->vm_start, offset_pages, area->vm_end - area->vm_start, PAGE_SHARED)){ printk(KERN_INFO "Failed to remap pfn range\n"); return -EAGAIN; } return 0; } ``` 这段代码展示了如何利用`remap_pfn_range()`辅助函数把连续的一组物理帧关联起来形成一块可供直接寻址的大块缓存区。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值