【问题标题】:computing physical addresses from virtual addresses in C从 C 中的虚拟地址计算物理地址
【发布时间】:2015-06-05 04:24:35
【问题描述】:

我正在努力编写一个将虚拟内存地址转换为物理地址的函数。我希望该函数返回一个虚拟 16 位地址的物理内存地址。

我只是使用忽略权限位(读、写等)的 16 位长虚拟地址。页表有 256 页,所以基表寄存器 BTR 只指向该表。

我希望函数返回:

    Success: the physical address (a void*)
    Page Fault: the virtual page number (an integer)
    Protection Fault: a copy of the PTE

这是页表项:

    31    30..28 27..............................................4 3 2 1 0
   +-----+------+-------------------------------------------------+-+-+-+-+
   |Valid|unused| 24-bit Physical Page Number                     |P|R|W|X|
   +-----+------+-------------------------------------------------+-+-+-+-+

我正在尝试了解虚拟内存的工作原理。我对如何获取 16 位虚拟地址并将其映射到 32 位页表条目以及如何从那里获取物理地址感到困惑。我已经定义了result_t 和下面的页表条目的联合,但我不确定如何使用它们。我从网上看得到了很多帮助,但一切都变得混乱了,我只想了解一切是如何直接运作的。

以下是一些必要的定义:

  extern void* BTR;

  typedef struct result_st {
     enum {SUCCESS, PAGEFAULT,
                PROTFAULT, NOTIMPLEMENTED} status;
     union {
         void* pa;
         unsigned vpn;
         unsigned pte;
     } value;

    } result_t;
  static result_t success(void* pa) {

      result_t res;
      res.status=SUCCESS;
      res.value.pa = pa;
      return res;
    }

  typedef union {
      unsigned All;
      struct {
        unsigned Valid               :1;
        unsigned Unused              :3;
        unsigned PhysicalPageNumber  :24;
        unsigned SupervisoryMode     :1;
        unsigned Read                :1;
        unsigned Execute             :1;
        unsigned Write               :1;

      };
    } PageTableEntry;

  static int is_valid (unsigned pte) {

        return 1 & (pte >> 31);
      }

这是我正在编写的函数:

result_t legacy(unsigned short va)
{
  result_t result;
  unsigned pte = va << 8;
  result.value.pte = pte;
  // This is my attempt so far.
  // I want to use is_Valid() somewhere

  void* pa = pte >> 8 | (va & 255);
    return success(pa);
}

感谢您提供的任何建议!

【问题讨论】:

  • 页表可能有不同类型,二级、三级等。
  • 不是这个,只有一个。
  • 不是很依赖平台吗?我希望是的,所以你应该把平台标签和位数。它不能只是通用的 C 问题
  • 我提供了地址的页表项、位数和位长。这不是特定于平台的,我正在定义平台。
  • @slippeel:那么它可能设计得很糟糕。如果我假设 32 位虚拟地址和 32 位物理地址,那么您的 page number 需要向左移动,因为它位于错误的位置(例如 phys = ((entry &amp; mask) &lt;&lt; 8) + offset 而不是 phys = (entry &amp; mask) + offset)并且 page number 应该是第 12 位改为页表条目的 31。除此之外,每个进程/虚拟地址空间都需要一个完整的页表,这将花费 4 MiB(即使它们只使用其虚拟地址空间的一小部分);所以 ~100 个进程 = ~400 MiB 的 RAM,大部分被浪费了。

标签: c virtual-memory


【解决方案1】:

您缺少实际页表的定义。我假设它是这样的(假设我正确理解了你的问题):

#define PAGE_TABLE_SIZE 256
PageTable page_table[PAGE_TABLE_SIZE];

那么您的代码将如下所示:

#define VIRT_PAGE_SIZE_BITS 8

/* Get virtual page number by dividing by the virt page size */
unsigned int virt_page_num = va >> VIRT_PAGE_SIZE_BITS;

assert(virt_page_num < PAGE_TABLE_SIZE); // Or do proper error handling

/* Use virtual page number to index into page table */
PageTableEntry pte = page_table[virt_page_num];
if (is_valid(pte)) {
    if (is_access_ok(pte)) {
        unsigned int phys_page_num = pte.PhysicalPageNumber;
        return success(phys_page_num);
    } else {
        /* Protection fault code goes here */
    }
} else {
    /* Page fault code goes here */
}

【讨论】:

  • 感谢您的回复。当我实现这个时,我得到了unsigned int phys_page_num = pte.PhysicalPageNumber 的错误,因为它试图将一个 int 转换为 void*,我将如何编写它以使其变为 void*?
  • void * 用于虚拟地址(实际可用的指针)。物理地址不是可用的指针,不应这样对待。请改用uint32_t 或适当的名称。
猜你喜欢
  • 2015-05-04
  • 2013-05-05
  • 1970-01-01
  • 2021-04-17
  • 2017-07-07
  • 2014-03-15
  • 2012-02-18
  • 1970-01-01
相关资源
最近更新 更多