轉自:https://blog.csdn.net/sunlei0625/article/details/59476987
版權聲明:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
本文鏈接:https://blog.csdn.net/sunlei0625/article/details/59476987
首先我們基於平坦型物理內存,單個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的頁描述符。
————————————————
版權聲明:本文為CSDN博主「星空探索」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/sunlei0625/article/details/59476987