我如何实现分页,并通过虚拟地址找到物理内存地址

问题描述 投票:4回答:1

我想实现分页的初始化。关于osdev Wiki的一些链接:https://wiki.osdev.org/Paginghttps://wiki.osdev.org/Setting_Up_Paging,我自己的版本非常不同。因为,当我们查看page directory时,他们说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
投票

假设您正在尝试标识物理地址空间的前4个MiB:

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

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

c)对于__PAGE_DIRECTORY__[0] = ((unsigned int)__FIRST_PAGE_TABLE__ << 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位更重要)。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.