Linux 核心虛擬位址到實體地址轉換討論

2021-07-27 16:58:31 字數 2971 閱讀 9986

首先我們基於平坦型物理記憶體,單個node,下面是基於64位armv8架構得到,其他架構也有類似結論:

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

ffffffc001c82000 b _end

arm:

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)

如經典32為x86和arm為0xc0000000,即3gb處。對於64位來說,一般為0xffffffc000000000

比如說,如果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,

= 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的頁描述符。

Linux 核心空間虛擬位址和實體地址相互轉換

個人學習筆記,可能會有錯誤之處,敬請諒解。一直以來感覺虛擬位址和實體地址之間的相互轉換非常麻煩,虛擬位址到實體地址的轉換由cpu硬體完成,但實體地址到虛擬位址怎麼轉換啊?況且有時候在軟體上也希望通過乙個虛擬位址得到實體地址,這時候自然不能依賴cpu的硬體了,該怎麼轉換呢?define va x vo...

linux實體地址和虛擬位址定義

線性位址 linear address 是邏輯位址到物理位址變換之間的中間層 如果啟用了分頁機制,那麼線性位址可以再經過變換以產生乙個實體地址 如果沒有啟用分頁機制,那麼線性位址直接就是實體地址 分頁管理 cpu的頁式記憶體管理單元,負責把乙個線性位址,最終翻譯為乙個實體地址 例如乙個32位的機器,...

虛擬位址與實體地址

乙個程式編譯連線後形成的位址空間是乙個虛擬位址空間,但是程式最終還是要執行在物理記憶體中。因此,應用程式所給出的任何虛位址最終必須被轉化為實體地址,所以,虛擬位址空間必須被對映到物理記憶體空間中,這個對映關係需要通過硬體體系結構所規定的資料結構來建立。這就是我們所說的段描述符表和頁表,linux主要...