nopage()方法实现

时间:2013-01-28 10:58:45

标签: linux linux-device-driver

任何人都知道如何在没有页面方法的情况下将虚拟地址转换为物理地址。 参考设备驱动程序书,nopage方法为,

struct page *simple_vma_nopage(struct vm_area_struct *vma,
                unsigned long address, int *type)
{
    struct page *pageptr;
    unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
    unsigned long physaddr = address - vma->vm_start + offset;
    unsigned long pageframe = physaddr >> PAGE_SHIFT;

    if (!pfn_valid(pageframe))
        return NOPAGE_SIGBUS;
    pageptr = pfn_to_page(pageframe);
    get_page(pageptr);
    if (type)
        *type = VM_FAULT_MINOR;
    return pageptr;
}

page_shift是用于虚拟和物理内存地址的重复偏移的位数。 但是什么是偏移变量? 如何通过对地址和vm_start等虚拟地址变量的算术运算来计算物理地址?

3 个答案:

答案 0 :(得分:2)

我觉得vm_pgoff的文档不是很清楚。 这是RAM中存储区的第一页的偏移量。 因此,如果我们的RAM从0x00000000开始,并且我们的内存区域开始 在0x0000A000,然后vm_pgoff = 10.如果您考虑/重新访问mmap 系统调用然后你可以看到我们传递的“偏移量”是偏移量 文件中的起始字节,将从中映射“length”字节 到内存区域。该偏移量可以左转换为地址 将其转换为PAGE_SHIFT值12(即每页大小4KB)

现在,无论cr3寄存器是否用于线性地址 物理地址转换与否,当我们说“地址 - vm_start” 然后这给出了地址之间的部分大小。 例:     vm_start = 0xc0080000     地址= 0xc0090000

address - vm_start = 0x00010000
physaddr    = (address - vma->vm_start) + offset;
            = 0x00010000 + (10 << PAGE_SHIFT)
            = offset_to_page_that_fault + start_addr_of_memoryRegion_in_RAM  
            = 0x00010000 + 0x0000A000
            = 0x0001A000

现在因为这是物理地址,所以我们需要转换为页面框架 通过PAGE_SHIFT值右移的数字,即0x0001A000&gt;&gt; 12 = 0x1A = 26(十进制) 因此,必须使用正在映射的文件中的数据加载第26页的帧。 因此,使用inode的struct address_sapce从磁盘中检索数据 其中包含磁盘上页面位置的信息(交换空间)。 一旦数据被引入,我们将返回表示此数据的struct页面 发生此页面错误的page_frame。我们将此内容返回给用户。

这是我对最新版本的理解,但我还没有测试过。

答案 1 :(得分:0)

“simple_region_start”是从开始的偏移量 我们的子区域需要映射的物理内存

实施例:     off =物理内存的开始(页面对齐)= 0xd000 8000     simple_region_start = 0x1000     因此子区域开始的物理地址     我们要映射的是= 0xd000 8000 + 0x1000               = 0xd000 9000

现在虚拟大小是需要从中映射的部分 物理内存可用。这必须由用户正确定义。

simple_region_size =指向最后一个的物理地址 我们需要绘制的部分。

因此,如果我们希望从可用的物理内存映射8KB 然后是如何进行计算

simple_region_size = physical address just beyond the last of our portion
simple_region_size = 0xd000 9000 + 0x2000 (for the 8KBs)    
simple_region_size = 0xd000 B000

所以我们的8KB部分将从物理地址[0xd000 B000到0xd000 9000]不等     因此物理大小,即psize = 0x2000

我们进行健全检查,即 如果我们的物理内存部分的大小较小 比用户尝试使用全长映射的内容 这个内存区域的虚拟地址范围,那么我们 举一个例外。也就是说。 vsize = 0x3000

否则我们使用API​​“remap_pfn_range”来映射 传入的物理内存部分 物理地址而不是页面帧号 之前已完成,因为这是IO内存。 我觉得应该是API“io_remap_page_range” 这里是前面提到的。

因此它将映射物理内存开始的部分 从用户的物理地址0xd000 9000 线性地址从vsma的vma-> vm_start开始。

N.B和以前一样,我还没有测试过这个!

答案 2 :(得分:0)

不,书中的陈述是正确的,因为 如前所述,“物理”只是地址 开始您想要的地区/部分 映射出来的物理内存 从“关闭”物理地址到“simple_region_size” “simple_region_size”值由用户决定。 类似地,“simple_region_start”由用户决定。

simple_region_start >= off

因此用户可以映射的最大物理内存 由以下决定:psize = simple_region_size - off 即从物理记忆开始到结束 部分。

但实际上将用这个内存映射多少 区域由“vma-&gt; vm_end - vma-&gt; vm_start”给出,并且 由vsize代表。因此存在执行的需要 用户可以获得更多的健全性检查 意图。

Kind regards,
Sanjeev Ranot