Arm Linux 內(nèi)核頁表的段式映射
映射表(PGD)從虛擬地址0xc0004000開始,每項(xiàng)4字節(jié),每項(xiàng)對應(yīng)1M內(nèi)存空間,每項(xiàng)的高12位就是這1M內(nèi)存的高12位地址。
一開始,內(nèi)核不會為所有內(nèi)存建立映射,只會映射必要的一部分,這部分代碼在arch/arm/kernel/head.S中由匯編代碼完成。
以S3C6410為例,下面是在剛剛進(jìn)入start_kernel()后打印出來的一段內(nèi)核映射表。注意內(nèi)核自身的映射表項(xiàng)是從0xc0007000地址開始。因?yàn)閺?xc0004000開始的是整個(gè)4G空間的表,內(nèi)核內(nèi)存只占最高的那1G,所以要加一個(gè)偏移量:3G/1M * 4bytes = 0x3000。
c0007000: 0e 0c 00 50 0e 0c 10 50 0e 0c 20 50 00 00 00 00
c0007010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
c0007020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
c0007030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
由于字節(jié)序的原因,上面的每一個(gè)表項(xiàng)應(yīng)該顛倒一下順序來看,以第一項(xiàng)為例,應(yīng)該是:
50 00 0c 0e
高12位地址是0x500,因?yàn)閟3c6410的內(nèi)存物理地址就是從0x50000000開始。
啟動到C函數(shù)start_kernel()之后,在arch_setup()中會重寫映射表,映射所有內(nèi)存。這時(shí)的頁表會如下所示:
c0007000: 0e 04 00 50 0e 04 10 50 0e 04 20 50 0e 04 30 50
c0007010: 0e 04 40 50 0e 04 50 50 0e 04 60 50 0e 04 70 50
c0007020: 0e 04 80 50 0e 04 90 50 0e 04 a0 50 0e 04 b0 50
c0007030: 0e 04 c0 50 0e 04 d0 50 0e 04 e0 50 0e 04 f0 50
......
注意,每一個(gè)表項(xiàng)的最后兩個(gè)bit指明了映射方式,00表示段式映射。在內(nèi)存重新映射之后,這一映射方式并沒有變化。
再來看一段應(yīng)該程序的pgd表內(nèi)容,這段內(nèi)容是從其pgd表開始位置打印的,所以是為用戶程序虛擬進(jìn)程空間建立的映射:
c0d98000: 31 c8 d8 50 31 cc d8 50 00 00 00 00 00 00 00 00
c0d98010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
c0d98020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
c0d98030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
可以看到,表項(xiàng)最后兩位是01,這已經(jīng)是二級頁式映射了。
這說明,盡管內(nèi)核為應(yīng)用程序建立的是二級粗粒度頁式映射,但Linux內(nèi)核自身一直是運(yùn)行在段映射模式下。兩種映射在同一張pgd表里面可以同時(shí)使用,映射方式不必全表統(tǒng)一。
評論