Arm Linux 內核頁表的段式映射
映射表(PGD)從虛擬地址0xc0004000開始,每項4字節(jié),每項對應1M內存空間,每項的高12位就是這1M內存的高12位地址。
一開始,內核不會為所有內存建立映射,只會映射必要的一部分,這部分代碼在arch/arm/kernel/head.S中由匯編代碼完成。
以S3C6410為例,下面是在剛剛進入start_kernel()后打印出來的一段內核映射表。注意內核自身的映射表項是從0xc0007000地址開始。因為從0xc0004000開始的是整個4G空間的表,內核內存只占最高的那1G,所以要加一個偏移量: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é)序的原因,上面的每一個表項應該顛倒一下順序來看,以第一項為例,應該是:
50 00 0c 0e
高12位地址是0x500,因為s3c6410的內存物理地址就是從0x50000000開始。
啟動到C函數start_kernel()之后,在arch_setup()中會重寫映射表,映射所有內存。這時的頁表會如下所示:
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
......
注意,每一個表項的最后兩個bit指明了映射方式,00表示段式映射。在內存重新映射之后,這一映射方式并沒有變化。
再來看一段應該程序的pgd表內容,這段內容是從其pgd表開始位置打印的,所以是為用戶程序虛擬進程空間建立的映射:
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
可以看到,表項最后兩位是01,這已經是二級頁式映射了。
這說明,盡管內核為應用程序建立的是二級粗粒度頁式映射,但Linux內核自身一直是運行在段映射模式下。兩種映射在同一張pgd表里面可以同時使用,映射方式不必全表統(tǒng)一。
評論