内核的早期页表

本文探讨了x86平台内核启动过程中的早期页表设置,从cr3寄存器开始,详细解释了early_level4_pgt的结构和映射关系,以及如何启用虚拟地址进行地址转换。通过分析,揭示了内核代码空间页表的配置和工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

刚才的那张页表实在是太粗糙了,我想可能是有什么历史原因导致的吧。到了真正的内核,怎么也得换个漂亮点的页表。

这次的页表就在arch/x86/kernel/head_64.S里面咯~

从cr3开始

在x86平台上保存页表起始地址的就是这个cr3寄存器了,那就先看看这个寄存器变成了谁呗。

    movq    $(early_level4_pgt - __START_KERNEL_map), %rax

    /* Setup early boot stage 4 level pagetables. */
    addq    phys_base(%rip), %rax
    movq    %rax, %cr3

嗯,这个东西稍微有点绕。phys_base是一个偏移,我们暂且认为就是0吧。所以这次加载到cr3中的地址是early_level4_pgt - __START_KERNEL_map。这里稍微解释一下下,如果实在理解不了,暂时先跳过。

加载到cr3的是一个物理地址,而我们编译出的内核vmlinux是一个ELF文件且最终会在虚拟地址空间运行,所以所有的符号都保存的是虚拟地址。上面这两个动作就是得到early_level4_pgt这个符号在运行时的物理地址。

不管怎么样知道加载到cr3的地址是指向early_level4_pgt的就好~

early_level4_pgt的容貌

刚才最简单的页表也有三层了,内核中的页表也不例外。

early_level4_pgt:

NEXT_PAGE(early_level4_pgt)
    .fill   511,8,0
    .quad   level3_kernel_pgt - __START_KERNEL_map + _PAGE_TABLE

level3_kernel_pgt

NEXT_PAGE(level3_kernel_pgt)
    .fill   L3_START_KERNEL,8,0
    /* (2^48-(2*1024*1024*1024)-((2^39)*511))/(2^30) = 510 */
    .quad   level2_kernel_pgt - __START_KERNEL_map + _KERNPG_TABLE
    .quad   level2_fixmap_pgt - __START_KERNEL_map + _PAGE_TABLE

level2_kernel_pgt

NEXT_PAGE(level2_kernel_pgt)
    /*
     * 512 MB kernel mapping. We spend a full page on this pagetable
     * anyway.
     *
     * The kernel code+data+bss must not be bigger than that.
     *
     * (NOTE: at +512MB starts the module area, see MODULES_VADDR.
     *  If you want to increase this then increase MODULES_VADDR
     *  too.)
     */
    PMDS(0, __PAGE_KERNEL_LARGE_EXEC,
        KERNEL_IMAGE_SIZE/PMD_SIZE)

是不是又看得头晕了? 嗯,不着急,再来看一张图~

这里写图片描述

所以这其实是一张编译时就写好的页表。这样看,是不是简单了些?

映射关系

细心的朋友可能发现了,这张页表和之前的页表最后一个层级差不多,就是少了点。最大的差别是第一层上我们使用了最后一级的表项,而第一章表中使用的是第一级。

对了,我想你猜到了。这就是虚拟地址和物理地址的映射了。

那我们来算一下这次映射关系究竟是什么样子的。

    (511) << 39 | (510) << 30
=   FFFF 8000 0000

再来看一下变量__START_KERNEL_map的定义

#define __START_KERNEL_map  _AC(0xffffffff80000000, UL)

x86上只使用了48bit虚拟地址空间,而不是64bit。所以这两个地址就是等价的。(或许还漏了点什么,以后理解了再来补吧~)

经过计算证实了这次映射的就是内核代码空间的页表。

启用虚拟地址

正如之前看到的,虽然使用了页表,但是页表中的物理地址和虚拟地址是一模一样的。经过了这次页表的改造,就有了真正的地址转换了。

代码很有意思

    /* Ensure I am executing from virtual addresses */
    movq    $1f, %rax
    jmp *%rax
1:

就是取到下一个lable的地址,然后跳过去~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值