linux启动页表x86_64,引导期间Linux内核空间中的页表

CodingNow..

17

以下讨论基于32位ARM Linux,内核源代码版本为3.9

如果您完成设置初始页面表(稍后将被功能覆盖paging_init)和转向的过程,则可以解决所有问题.在MMU上.

当bootloader首次启动内核时,Assembly函数stext(在arch\arm\kernel\head.s中)是第一个运行的函数.请注意,此时MMU尚未开启.

除此之外,此功能完成的两个导入作业stext是:

创建初始页面tabel(稍后将被函数覆盖paging_init)

打开MMU

跳转到C部分内核初始化代码并继续

在深入研究你的问题之前,了解一下是有益的:

在MMU打开之前,CPU发出的每个地址都是物理地址

MMU打开后,CPU发出的每个地址都是虚拟地址

在打开MMU之前应该设置一个正确的页面表,否则你的代码将被"吹走"

按照惯例,Linux内核使用较高的1GB部分虚拟地址,而用户域使用较低的3GB部分

现在棘手的部分:

第一招:使用与位置无关的代码.汇编函数stext链接到地址" PAGE_OFFSET + TEXT_OFFSET"(0xCxxxxxxx),这是一个虚拟地址,但是,由于MMU还没有打开,汇编函数stext运行的实际地址是" PHYS_OFFSET + TEXT_OFFSET"(实际值取决于你的实际值)硬件),这是一个物理地址.

所以,事情就是这样:函数程序stext"认为"它在0xCxxxxxxx这样的地址运行,但它实际上是在地址(0x00000000 + some_offeset)中运行(比如你的硬件配置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"指令是伪指令,意思是"获取函数__mmap_switched的(虚拟)地址并将其放入r13"

函数__enable_mmu依次调用函数__turn_mmu_on :(注意我从函数__turn_mmu_on中删除了几条指令,它们是函数的基本指令,但不是我们感兴趣的)

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)

第二招:在打开MMU之前设置初始页面表时的相同映射.更具体地说,运行内核代码的相同地址范围被映射两次.

正如预期的那样,第一个映射将地址范围0x00000000(再次,此地址取决于硬件配置)映射到(0x00000000 + offset)到0xCxxxxxxx到(0xCxxxxxxx + offset)

有趣的是,第二个映射将地址范围0x00000000到(0x00000000 + offset)映射到自身(即:0x00000000 - >(0x00000000 + offset))

为什么这样做?请记住,在MMU打开之前,CPU发出的每个地址都是物理地址(从0x00000000开始),MMU打开后,CPU发出的每个地址都是虚拟地址(从0xC0000000开始).

因为ARM是一个流水线结构,所以在MMU打开的那一刻,ARM的管道中仍然有指令使用在MMU打开之前由CPU生成的(物理)地址!为了避免这些指令被炸毁,必须设置相同的映射来满足它们.

现在回到你的问题:

这时,在打开页表后,内核空间仍然是1GB(从0xC0000000 - 0xFFFFFFFF)?

A:我想你的意思是打开MMU.答案是肯定的,内核空间是1GB(实际上它也占用0xC0000000以下的几兆字节,但这不是我们感兴趣的)

在内核进程的页表中,只映射了0xC0000000 - 0xFFFFFFFF范围内的页表项(PTE)?PTE超出此范围将不会映射,因为内核代码永远不会跳转到那里?

答:虽然这个问题的答案非常复杂,但它涉及到有关特定内核配置的大量细节.

要完全回答这个问题,您需要阅读设置初始页表(汇编函数__create_page_tables)的内核源代码部分和设置最终页表(C函数paging_init)的函数.

简单来说,ARM中有两级页表,第一页表是PGD,占用16KB.内核在初始化过程中首先将此PGD归零,并在汇编函数中执行初始映射__create_page_tables.在功能上__create_page_tables,只映射了很小一部分地址空间.

之后,在函数中设置最终页表paging_init,并且在该函数中,映射了相当大部分的地址空间.假如你只有512M RAM,对于大多数常见配置,这个512M-RAM将按内核代码逐段映射(1节为1MB).如果您的RAM非常大(例如2GB),则只会直接映射一部分RAM.(我将在此停止,因为关于问题2的细节太多)

打开页面表之前和之后的映射地址是一样的吗?

答:我想我已经在"第二招:在开启MMU之前设置初始页面时的相同映射"的解释中回答了这个问题.

4.内核空间中的页表是全局的,并且将在系统中的所有进程(包括用户进程)之间共享?

答:是的,不是.是的,因为所有进程共享内核页表的相同副本(内容)(更高的1GB部分).不,因为每个进程使用自己的16KB内存来存储内核页表(尽管每个进程的高1GB部分的页表内容是相同的).

5.这个机制在x86 32bit和ARM中是一样的吗?

不同的架构使用不同的机制

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值