【问题标题】:how can I implement paging , and find physical memory address knowing virtual address如何实现分页,并在知道虚拟地址的情况下找到物理内存地址
【发布时间】:2020-04-30 08:24:57
【问题描述】:

我想实现 paging 的初始化。 参考 osdev wiki 的一些链接:https://wiki.osdev.org/Paginghttps://wiki.osdev.org/Setting_Up_Paging,我自己的版本非常不同。 因为,当我们查看 页目录 时,他们说 12 位是标志位,其余位是页表地址,所以我尝试了这样的操作:

void init_paging() {
    unsigned int i = 0;

    unsigned int __FIRST_PAGE_TABLE__[0x400] __attribute__((aligned(0x1000)));

    for (i = 0; i < 0x400; i++) __PAGE_DIRECTORY__[i] = PAGE_PRESENT(0) | PAGE_READ_WRITE;

    for (i = 0; i < 0x400; i++) __FIRST_PAGE_TABLE__[i] = ((i * 0x1000) << 12) | PAGE_PRESENT(1) | PAGE_READ_WRITE;

    __PAGE_DIRECTORY__[0] = ((unsigned int)__FIRST_PAGE_TABLE__  << 12) | PAGE_PRESENT(1) | PAGE_READ_WRITE;

    _EnablingPaging_();
} 

这个功能帮助我知道物理地址知道虚拟地址:

void *get_phyaddr(void *virtualaddr) {
unsigned long pdindex = (unsigned long)virtualaddr >> 22;
unsigned long ptindex = (unsigned long)virtualaddr >> 12 & 0x03FF;

unsigned long *pd = (unsigned long *)__PAGE_DIRECTORY__[pdindex];

unsigned long *pt = (unsigned long *)pd[ptindex];

return (void *)(pt + ((unsigned int)virtualaddr & 0xFFF));

}

我走错方向了?

还是一样?

【问题讨论】:

    标签: c paging osdev


    【解决方案1】:

    假设您尝试标识映射物理地址空间的前 4 MiB:

    a) 对于unsigned int __FIRST_PAGE_TABLE__[0x400] __attribute__((aligned(0x1000)));,它是一个局部变量(例如,可能放在堆栈上);并且在函数返回后将无法生存(例如,它正在使用的堆栈空间稍后将被其他函数覆盖),导致页表损坏。这不太可能结束。

    b) 对于__FIRST_PAGE_TABLE__[i] = ((i * 0x1000) &lt;&lt; 12) | PAGE_PRESENT(1) | PAGE_READ_WRITE;,您将移动i 两次,一次是* 0x1000(与&lt;&lt; 12 相同),另一次是&lt;&lt; 12。这太多了,需要更像__FIRST_PAGE_TABLE__[i] = (i &lt;&lt; 12) | PAGE_PRESENT(1) | PAGE_READ_WRITE;

    c) 对于__PAGE_DIRECTORY__[0] = ((unsigned int)__FIRST_PAGE_TABLE__ &lt;&lt; 12) | PAGE_PRESENT(1) | PAGE_READ_WRITE;,地址已经是一个地址(而不是需要移动的“页码”),所以它需要更像__PAGE_DIRECTORY__[0] = ((unsigned int)__FIRST_PAGE_TABLE__) | PAGE_PRESENT(1) | PAGE_READ_WRITE;

    除此之外;我非常喜欢更好地使用类型。具体来说;您可能应该养成使用uint32_t(或uint64_t,或您自己的typedef)作为物理地址的习惯,以确保您不会意外地将虚拟地址与物理地址混淆(并确保当您出错时,编译器会抱怨错误的类型);因为(即使它现在不是很重要,因为你是身份映射)它将“很快”变得重要。我还建议将 uint32_t 用于页表条目和页目录条目,因为它们必须是 32 位,而不是“编译器认为 int 应该是什么大小”(请注意,这与您的想法不同关于代码,这比编译器实际执行的操作或 int 是否恰好是 32 位更重要)。

    【讨论】:

    • 甚至可能值得将 typedef struct { uint32_t addr; } physaddr_t;typedef struct { uint32_t addr; } virtaddr_t; 设为两种不同的类型,如果您不小心混淆了它们,编译器会抱怨它们。
    • 还有一个问题,就是知道物理地址知道虚拟地址void *get_phyaddr(void *virtualaddr);。我们知道,当我们放置未映射的virtaul地址时,应该会发生pagingFault...但是什么也没做...问题出在哪里??
    • @ledoux: uint64_t get_phyaddr(void *virtualaddr) 将使用页表(与 CPU 相同) - 例如找到正确的页目录条目并检查该信息,然后(可能)找到正确的页表条目并检查该信息,然后返回“没有物理地址”状态或“您没有权限”的物理地址" status.uint64_t get_phyaddr(void *virtualaddr) 如果页面不存在,则不应导致页面错误(您只希望在访问页面时出现页面错误)。
    • @Brendan,看看这个链接github.com/kouamdo/PEPPER_OS/blob/…,完美吗??但是,当我检查一些虚拟地址时,pageFault interrupt 不会出现
    • @ledoux:您没有检查页面目录条目或页表条目中的任何标志。例如。如果页目录条目显示“页表不存在”,那么您只需假设页表存在(并且位于物理地址 0x00000000)并尝试访问它(这将起作用,因为它的地址错误),而不是向调用者返回任何类型的“那个虚拟地址没有物理地址”错误。
    【解决方案2】:

    当我们询问 page ,但 page 不存在时,我们有 pageFault Interrupt 。 所以为了避免这种情况,我们可以检查页面是否存在,否则我选择返回0x0

    physaddr_t *get_phyaddr(void *virtualaddr) {
        uint32_t pdindex = (uint32_t)virtualaddr >> 22;
        uint32_t ptindex = (uint32_t)virtualaddr >> 12 & 0x03FF;
    
        uint32_t *pd, *pt, ptable;
    
        if ((page_directory[pdindex] & 0x3) == 0x3) {
            pd = (uint32_t *)(page_directory[pdindex] & 0xFFFFF000);
    
            if ((pd[ptindex] & 0x3) == 0x3) {
                ptable = pd[ptindex] & 0xFFFFF000;
    
                pt = (uint32_t *)ptable;
    
                return (physaddr_t *)(pt + ((uint32_t)(virtualaddr)&0xFFF));
            } else
                return 0x0;
        } else
            return 0x0;
    }
    

    【讨论】:

      猜你喜欢
      • 2014-09-06
      • 1970-01-01
      • 2015-05-04
      • 1970-01-01
      • 2015-07-09
      • 2010-09-26
      • 1970-01-01
      • 2013-05-05
      • 2014-05-18
      相关资源
      最近更新 更多