首先我們基于平坦型實體記憶體,單個node,下面是基于64位ARMv8架構得到,其他架構也有類似結論: 首先我們知道在我們成功編譯好kernel後會生成一個system.map檔案,其給出了核心整個虛拟位址空間情況,比如:
ARM64: 整個核心空間起始位址: ffffffc000080000 T _text
代碼段起始位址: ffffffc000080160 T stext
異常向量表位址: ffffffc000083000 T vectors
ffffffc0010890b8 B __bss_start ffffffc0010890b8 D _edata
ffffffc00191ab58 B mem_map ffffffc00191ab60 B max_mapnr
ffffffc001c7dd28 B __bss_stop ffffffc001c7e000 B idmap_pg_dir ffffffc001c80000 B swapper_pg_dir ffffffc001c82000 B _end
ARM:
c0003000 A swapper_pg_dir c0008000 T _text c0008000 T stext c0008090 t __create_page_tables c0008168 t __turn_mmu_on_loc c0008174 T secondary_startup c00081e0 T __secondary_switched c00081ec t __secondary_data c00081f8 t __enable_mmu c0008220 t __vet_atags c0008280 T __exception_text_start c0008280 T _stext c0008280 T asm_do_IRQ c0008284 T do_undefinstr
c0caccb8 b suspend_time c0caccc0 b last_transmit c0caccc8 b activity_lock c0caccd0 b klist_remove_lock c0caccd4 B __bss_stop c0caccd4 B _end
對于ARM來說,32位和64位明顯不同。
對整個核心空間代碼和資料來說,由于他們是直接映射的,我們這裡讨論起來非常簡單
對于這些位址,核心通過宏__pa()找到這些虛拟位址對應的實體位址。或者通過__va()找到實體位址對應
的虛拟位址。
arch/arm64/include/asm/memory.h #define __pa(x) __virt_to_phys((unsigned long)(x)) #define __va(x) ((void *)__phys_to_virt((phys_addr_t)(x))) #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) #define virt_to_pfn(x) __phys_to_pfn(__virt_to_phys(x))
繼續看定義:
include/asm-generic/memory-module.h #define __virt_to_phys(x) (((phys_addr_t)(x) - PAGE_OFFSET + PHYS_OFFSET)) #define __phys_to_virt(x) ((unsigned long)((x) - PHYS_OFFSET + PAGE_OFFSET))
#define __phys_to_pfn(paddr) ((unsigned long)((paddr) >> PAGE_SHIFT)) #define __pfn_to_phys(pfn) ((phys_addr_t)(pfn) << PAGE_SHIFT)
#define __pfn_to_page(pfn) (mem_map + ((pfn) - ARCH_PFN_OFFSET)) #define __page_to_pfn(page) ((unsigned long)((page) - mem_map) + ARCH_PFN_OFFSET)
這裡需要注意PAGE_OFFSET和PHYS_OFFSET定義,前者是整個核心空間開始的虛拟位址,一般跟體系結構相關,
如經典32為X86和ARM為0xC0000000,即3GB處。對于64位來說,一般為0xFFFFFFC000000000 而PHYS_OFFSET則為實體記憶體起始位址,一般來說,這個偏移跟晶片設計相關,這個偏移表示了通路DDR最低的位址線。
比如說,如果PHYS_OFFSET為1GB,DDR大小為2GB,那麼有效通路DDR空間的位址必須是
0x40000000--0xC0000000之間。
在我們上面給出的system.map中的虛拟位址可以看到, 核心虛拟位址起始位址起始為0xFFFFFFC000000000,這個由核心定義的PAGE_OFFSET給出。 而核心真正開始的使用的位址為0xffffffc000080000這裡有個偏移量,大小為128個頁(4KB)。總大小為512kB位元組。
在本實驗上測試,得到PHYS_OFFSET為0x8800000,即136MB開始處。
可以看到,一個核心中的虛拟位址,即0xFFFFFFC000000000以上的位址,如果尋找其實體位址,使用__pa()宏非常簡單,
比如上面的mem_map的虛拟位址為0xffffffc00191ab58,其減去起始虛拟位址0xFFFFFFC000000000後為0x0191ab58,
然後根據實體位址偏移情況,得到真正的實體位址:0x0191ab58 + PAGE_OFFSET = 0x0191ab58 + 0x8800000
= 0xA11AB58 可以看到,mem_map的實體位址為0xA11AB58。 那麼對于這個實體位址,對應的頁幀号為多少呢?即PFN為多少,這時需要使用上面的__phys_to_pfn(). 可以看到,直接進行右移即可。例如對于上面的mem_map來說,其頁幀為0xA11A。
那麼對于一個PFN,其對應的實體page描述符是多少呢?這時需要使用宏__pfn_to_page(),注意這裡使用的 ARCH_PFN_OFFSET,其就是偏移PHYS_OFFSET對應的頁幀号,上面為0x8800000則對應頁幀為0x8800,則上面mem_map對應的頁幀号 為0xA11A - 0x8800 = 0x191A ,然後再根據mem_map數組,即 mem_map[0x191A]為mem_map的頁描述符。