Linux 內核虛擬地址到物理地址轉換討論【轉】


轉自: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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM