【问题标题】:Translate virtual address to physical from multi-level pagetabels将虚拟地址从多级页表转换为物理地址
【发布时间】:2019-02-28 10:20:15
【问题描述】:

我正在尝试将虚拟内存地址转换为物理地址,但无法正常工作。我目前正在做一个操作系统作为作业,现在我必须为用户模式实现一个 printf 函数,所以当你调用 write 系统调用时,系统应该将用户模式下的数组内容打印到串口(现在),为此我必须将地址从虚拟地址转换为物理地址。

这是我来自系统调用处理程序的代码:

Pcb* pcb = getCR3(); // contains the page directory for usermode
setCR3(kernelPageDir); // set the CR3 register to the kernel page directory

uint32_t tableNum = (vAddr >> 22) & 0x3ffUL; // get the upper 10 bits
uint32_t pageIndex = (vAddr >> 12) & 0x3ffUL // get the middle 10 bits
uint32_t offset = vAddr & 0xfffUL; // get the 12 lower bits

uint32_t* topTable = pcb->pageDirectory[tableNum]; // Access the top level table
uint32_t lowTable = topTable[pageIndex]; // Entry to the 2nd table
uint32_t* addr = lowTable + offset; // Should be the physical address

serialPrintf("Structure: tableNum=%08x pageIndex=%08x  offset=%08x\n", tableNum, pageIndex, offset);
serialPrintf("Address: topTable=%08x lowTable=%08x addr=%08x\n",topTable, lowTable, addr);
serialPrintf("Char:%c", (char*)addr[0]);

当我运行代码时,它在尝试访问它的值时给我一个页面错误:

Structure: tableNum=00000020 pageIndex=00000048  offset=00000378
Address: topTable=00000000 lowTable=0015d000 addr=0015d378
Page fault! errcode=00000000 addr=0015d378

这是书中解释页面结构的部分:

【问题讨论】:

  • (假设您的其他代码是正确的...)您不能在访问内存时直接使用(即取消引用)物理地址。物理地址仅供硬件本身使用。所有代码(甚至内核模式代码)都使用虚拟地址来访问内存。没有办法绕过它。现在,假设数据页存在,访问实际数据将使用*((char *)vAddr) 完成(尽管实际上内核代码仅通过特定函数访问用户虚拟地址,以确保遵守用户模式寻址限制,并正确处理页面错误等等)。
  • 1.你如何获得内核 pgd 地址?它存储在 init_mm 结构中。我希望你能照顾好这个。 2.既然是系统调用处理程序,我希望这段代码运行在内核空间。 3. 不能直接在内核中取消引用物理地址。相反,您必须在每次表遍历时执行 kmap() 并使用该 VA 来遍历下一个表条目。最重要的是,对于您的用例,我没有看到任何强制页表遍历。您可以直接使用 *((char *)vAddr) (根据传递给自定义 printf 的参数对其进行类型转换)来打印值。

标签: c linux memory-management linux-kernel virtual-address-space


【解决方案1】:

如果您为一级表的最后一个(索引 1023)指向的二级表中的最后一个(索引 1023)条目填写 pte,则:

0xfffff000 .. 0xfffffffc will by an alias of the first level table,

0xffc00000 .. 0xffffffff will be an alias of the entire (sparse) page table.

例如:

int vtop(unsigned vaddr, unsigned *pa) {
    unsigned *pdtb = (unsigned *)0xfffff000;
    unsigned *pte = (unsigned *)0xffc00000;
    if (ptdb[vaddr>>20] & 1) {
         if (pte[vaddr>>12] & 1) {
              *pa = pte[vaddr>>12] &~0xfff;
              return 0;
         }
         return 2;
    }
    return 1;
}

您可以对任何索引执行此操作并调整指针值,但如果变得更加混乱,并且 0xffc/20 不碍事。

【讨论】:

    猜你喜欢
    • 2017-03-10
    • 2011-07-23
    • 2015-01-29
    • 2018-12-04
    • 1970-01-01
    • 1970-01-01
    • 2017-07-07
    • 2020-04-16
    相关资源
    最近更新 更多