2

任何人都知道如何在无页面方法中将虚拟地址转换为物理地址。参考设备驱动程序手册,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 是用于表示虚拟和物理内存地址偏移的位数。但是偏移变量是什么?如何通过对 address 和 vm_start 等虚拟地址变量的算术运算计算物理地址?

4

3 回答 3

2

感觉vm_pgoff的文档不是很清楚。这是内存区域第一页在 RAM 中的偏移量。因此,如果我们的 RAM 从 0x00000000 开始,并且我们的内存区域从 0x0000A000 开始,那么 vm_pgoff = 10。如果您考虑/重新访问 mmap 系统调用,那么您可以看到我们传递的“偏移量”是起始字节的偏移量“长度”字节将从中映射到内存区域的文件。该偏移量可以通过将其左移到 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 >> 12 = 0x1A = 26(十进制)因此第 26 页帧必须加载文件中的数据正在映射。因此,通过使用 inode 的结构 address_sapce 从磁盘检索数据,该结构包含磁盘上页面位置(交换空间)的信息。一旦数据被引入,我们返回结构页面,它在发生此页面错误的 page_frame 中表示此数据。我们将此返回给用户。

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

于 2013-01-29T18:08:50.703 回答
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 开始的那部分物理内存映射到从 vma->vm_start 的 vsize 开始的用户线性地址上。

注意:和以前一样,我还没有对此进行测试!

于 2013-02-03T13:14:21.477 回答
0

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

simple_region_start >= off

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

但实际上将映射到这个内存区域的数量由“vma->vm_end - vma->vm_start”给出,并由vsize表示。因此,存在执行完整性检查的需要,因为用户可以获得比预期更多的东西。

Kind regards,
Sanjeev Ranot
于 2013-02-04T08:41:42.717 回答