Linux kernel 引导期间Linux内核空间中的页表
我觉得Linux内核中的页表管理很混乱 在Linux内核空间中,在打开页表之前。内核将在具有1-1映射机制的虚拟内存中运行。打开页表后,内核将参考页表将虚拟地址转换为物理内存地址。 问题是:Linux kernel 引导期间Linux内核空间中的页表,linux-kernel,mmu,Linux Kernel,Mmu,我觉得Linux内核中的页表管理很混乱 在Linux内核空间中,在打开页表之前。内核将在具有1-1映射机制的虚拟内存中运行。打开页表后,内核将参考页表将虚拟地址转换为物理内存地址。 问题是: 此时,在打开页表之后,内核空间仍然是1GB(从0xC0000000到0xFFFFFF) 在内核进程的页表中,只映射0xC0000000-0xFFFFFFFF?范围内的页表条目(PTE)?。PTE超出此范围将不会被映射,因为内核代码永远不会跳转到那里 打开页表前后的映射地址相同? 例如,在打开页表之前,虚拟地
例如,在打开页表之前,虚拟地址0xC00000FF映射到物理地址0x000000FF,然后在打开页表之后,上述映射不会改变。虚拟地址0xC00000FF仍映射到物理地址0x000000FF。不同的是,在打开页表之后,CPU已经参考页表将虚拟地址转换为物理地址,而以前不需要这样做
当Linux启用MMU时,只需要映射内核空间的虚拟地址。这种情况在引导的早期发生。此时,没有用户空间。MMU可以将多个虚拟地址映射到同一物理地址没有任何限制。因此,在启用MMU时,最简单的方法是为内核代码空间和映射
link==phys
或0xC0000000进行virt==phys
映射
jmp
(跳转)或b
(分支)将从一个地址空间移动到另一个地址空间。此时,当我们在最终目标地址执行时,可以删除virt==phys
映射
我认为以上应该回答第1点到第3点。基本上,引导页面表不是最终页面表
四,。内核空间中的页表是全局的,将在系统中的所有进程(包括用户进程)中共享
是的,这是一个巨大的胜利,因为VIVT缓存和许多其他原因
五,。x86 32位和ARM中的这种机制相同吗
当然,基本的机制是不同的。即使对于这些系列中的不同处理器,它们也是不同的;486对P4对Amd-K6;ARM926与Cortex-A5与Cortex-A8等。但是,语义非常相似
请参阅:-一篇关于早期Linux内存阶段的文章
根据版本的不同,不同的内存池和页表映射在引导期间处于活动状态。在运行
init
之前,我们都熟悉的映射不需要就位。以下讨论基于32位ARM Linux,内核源代码版本为3.9如果您完成了设置初始页面表(稍后将由函数
paging_init
)和打开MMU的过程,您的所有问题都可以得到解决
当引导加载程序首次启动内核时,汇编函数stext
(在arch\arm\kernel\head.s中)是第一个运行的函数。请注意,MMU目前尚未打开
除其他外,此函数stext
完成的两个导入作业是:
- 创建初始页面选项卡(稍后将由
函数
)分页\u init
- 打开MMU
- 跳转到内核初始化代码的C部分并进行
- 在打开MMU之前,CPU发出的每个地址都是物理地址 地址
- 打开MMU后,CPU发出的每个地址都是虚拟地址
- 在打开MMU之前,应该设置一个合适的页表,否则您的代码将“被吹走”
- 按照惯例,Linux内核使用较高1GB部分的虚拟地址,而用户区使用较低的3GB部分
第一个技巧:使用位置无关代码。 汇编函数stext链接到地址“
PAGE\u OFFSET+TEXT\u OFFSET
”(0xCxxxxxxx),这是一个虚拟地址,但是,由于MMU尚未打开,汇编函数stext运行的实际地址是“PHYS\u OFFSET+TEXT\u OFFSET
”(实际值取决于实际硬件),这是一个物理地址
所以,这里有一件事:函数stext
的程序“认为”它运行在类似0xCxxxxxxx的地址中,但实际上它运行在地址(0x00000000+一些设置)中(假设您的硬件将0x00000000配置为RAM的起点)。因此,在启用MMU之前,需要非常仔细地编写汇编代码,以确保在执行过程中不会出错。实际上,使用了一种称为位置独立代码(PIC)的技术
为了进一步解释上述内容,我提取了几个汇编代码片段:
ldr r13, =__mmap_switched @ address to jump to after MMU has been enabled
b __enable_mmu @ jump to function "__enable_mmu" to turn on MMU
请注意,上面的“ldr”指令是一条伪指令,表示“获取函数uu mmap_的(虚拟)地址并将其放入r13”
和函数uu启用mmu依次调用函数u启用mmu:
(请注意,我从函数\uu\t中删除了几个指令。)
ENTRY(__turn_mmu_on)
mcr p15, 0, r0, c1, c0, 0 @ write control reg to enable MMU====> This is where MMU is turned on, after this instruction, every address issued by CPU is "virtual address" which will be translated by MMU
mov r3, r13 @ r13 stores the (virtual) address to jump to after MMU has been enabled, which is (0xC0000000 + some_offset)
mov pc, r3 @ a long jump
ENDPROC(__turn_mmu_on)