Linux內核ARM32內核內存布局圖【轉】


轉自:https://www.coolcou.com/linux-kernel/linux-kernel-memory-management/the-linux-kernel-arm32-kernel-memory-layout.html

Linux內核ARM32內核內存布局圖,Linux內核在啟動時會打印出內核內存空間的布局圖,下面是ARM Vexpress平台打印出來的內存空間布局圖:

 Virtual kernel memory layout: vector : 0xffff0000 - 0xffff1000 ( 4 kB) fixmap : 0xffc00000 - 0xfff00000 (3072 kB) vmalloc : 0xf0000000 - 0xff000000 ( 240 MB) lowmem : 0xc0000000 - 0xef800000 ( 760 MB) pkmap : 0xbfe00000 - 0xc0000000 ( 2 MB) modules : 0xbf000000 - 0xbfe00000 ( 14 MB) .text : 0xc0008000 - 0xc0658750 (6466 kB) .init : 0xc0659000 - 0xc0782000 (1188 kB) .data : 0xc0782000 - 0xc07b1920 ( 191 kB) .bss : 0xc07b1920 - 0xc07db378 ( 167 kB) 
C

這部分信息的打印是在mem_init()函數中實現的。

 [start_kernel->mm_init->mem_init] pr_notice("Virtual kernel memory layout:\n" " vector : 0x%08lx - 0x%08lx (%4ld kB)\n" " fixmap : 0x%08lx - 0x%08lx (%4ld kB)\n" " vmalloc : 0x%08lx - 0x%08lx (%4ld MB)\n" " lowmem : 0x%08lx - 0x%08lx (%4ld MB)\n" #ifdef CONFIG_HIGHMEM " pkmap : 0x%08lx - 0x%08lx (%4ld MB)\n" #endif #ifdef CONFIG_MODULES " modules : 0x%08lx - 0x%08lx (%4ld MB)\n" #endif " .text : 0x%p" " - 0x%p" " (%4td kB)\n" " .init : 0x%p" " - 0x%p" " (%4td kB)\n" " .data : 0x%p" " - 0x%p" " (%4td kB)\n" " .bss : 0x%p" " - 0x%p" " (%4td kB)\n", MLK(UL(CONFIG_VECTORS_BASE), UL(CONFIG_VECTORS_BASE) + (PAGE_SIZE)), MLK(FIXADDR_START, FIXADDR_END), MLM(VMALLOC_START, VMALLOC_END), MLM(PAGE_OFFSET, (unsigned long)high_memory), #ifdef CONFIG_HIGHMEM MLM(PKMAP_BASE, (PKMAP_BASE) + (LAST_PKMAP) * (PAGE_SIZE)), #endif #ifdef CONFIG_MODULES MLM(MODULES_VADDR, MODULES_END), #endif MLK_ROUNDUP(_text, _etext), MLK_ROUNDUP(__init_begin, __init_end), MLK_ROUNDUP(_sdata, _edata), MLK ROUNDUP( bss start, bss stop)) 
C

編譯器在編譯目標文件並且鏈接完成之后,就可以知道內核映像文件最終的大小,接下來打包成二進制文件,該操作由arch/arm/kernel/vmlinux.ld.S控制,其中也划定了內核的內存布局。
內核image本身占據的內存空間從_text段到_end段,並且分為如下幾個段。

  • 代碼段:_text和_etext為代碼段的起始和結束地址,包含了編譯后的內核代碼。
  • init段:__init_begin和__init_end為init段的起始和結束地址,包含了大部分模塊初始化的數據。
  • 數據段:_sdata和_edata為數據段的起始和結束地址,保存大部分內核的變量。
  • BSS段:__bss_start和__bss_stop為BSS段的開始和結束地址,包含初始化為0的所有靜態全局變量。

上述幾個段的大小在編譯鏈接時根據內核配置來確定,因為每種配置的代碼段和數據段長度都不相同,這取決於要編譯哪些內核模塊,但是起始地址_text總是相同的。內核編譯完成之后,會生成一個System.map文件,查詢這個文件可以找到這些地址的具體數值。

 figo# cat System.map ... c0008000 T _text ... c0658750 A _etext c0659000 A __init_begin ... c0782000 A __init_end c0782000 D _sdata ... c07b1920 D _edata c07b1920 A __bss_start ... c07db378 A __bss_stop c07db378 A _end ... 
C

內核模塊使用虛擬地址從MODULES_VADDR到MODULES_END的這段14MB大小的內存區域。

 #define MODULES_VADDR (PAGE_OFFSET - SZ_16M) /* * The highmem pkmap virtual space shares the end of the module area. */ #ifdef CONFIG_HIGHMEM #define MODULES_END (PAGE_OFFSET - PMD_SIZE) #else #define MODULES_END (PAGE_OFFSET) #endif 
C

用戶空間和內核空間使用3:1的划分方法時,內核空間只有1GB大小。這1GB的映射空間,其中有一部分用於直接映射物理地址,這個區域稱為線性映射區。在\2RM32平台上,物理地址[0:760MB]的這一部分內存被線性映射到[3GB:3GB+ 760MB]的虛擬地址上。線性映射區的虛擬地址和物理地址相差PAGE_OFFSET,即3GB。內核中有相關的宏來實現線性映射區虛擬地址到物理地址的查找過程,例如__pa(x)__va(x)

 [arch/arm/include/asm/memory.h] #define __pa(x) __virt_to_phys((unsigned long)(x)) #define __va(x) ((void *)__phys_to_virt((phys_addr_t)(x))) static inline phys_addr_t __virt_to_phys(unsigned long x) { return (phys_addr_t)x - PAGE_OFFSET + PHYS_OFFSET; } static inline unsigned long __phys_to_virt(phys_addr_t x) { return x - PHYS_OFFSET + PAGE_OFFSET; } 
C

其中,__pa()把線性映射區的虛擬地址轉換為物理地址,轉換公式很簡單,即用虛擬地址減去PAGE_OFFSET(3GB),然后加上PHYS_OFFSET(這個值在有的ARM平台上為0,在ARM Vexpress平台該值為0x6000_0000)。

那高端內存的起始地址(760MB)是如何確定的呢?

在內核初始化內存時,在sanity_check_meminfo()函數中確定高端內存的起始地址,全局變量high_memory來存放高端內存的起始地址。

 [arch/arm/mm/mmu.c] static void * __initdata vmalloc_min = (void *)(VMALLOC_END - (240 << 20) - VMALLOC_OFFSET); void __init sanity_check_meminfo(void) { phys_addr_t vmalloc_limit = __pa(vmalloc_min - 1) + 1; arm_lowmem_limit = vmalloc_limit; high_memory = __va(arm_lowmem_limit - 1) + 1; } 
C

vmalloc_min計算出來的結果是0x2F80_0000,即760MB。

為什么內核只線性映射760MB呢?剩下的264MB的虛擬地址空間用來做什么呢?

那是保留給vmalloc、fixmap和高端向量表等使用的。內核很多驅動使用vmalloc來分配連續虛擬地址的內存,因為有的驅動不需要連續物理地址的內存;除此以外,vmalloc還可以用於高端內存的臨時映射。一個32bit系統中實際支持的內存數量會超過內核線性映射的長度,但是內核要具有對所有內存的尋找能力。

 /* * Just any arbitrary offset to the start of the vmalloc VM area: the * current 8MB value just means that there will be a 8MB "hole" after the * physical memory until the kernel virtual memory starts. That means that * any out-of-bounds memory accesses will hopefully be caught. * The vmalloc() routines leaves a hole of 4kB between each vmalloced * area for the same reason. ; ) */ #define VMALLOC_OFFSET (8*1024*1024) #define VMALLOC_START (((unsigned long)high_memory + VMALLOC_ OFFSET) & (VMALLOC_OFFSET-1)) #define VMALLOC END 0xff000000UL 
C

vmalloc區域在ARM32內核中,從VMALLOC_START開始到VMALLOC_END結束,即從0xf000_0000到0xff00_0000,大小為240MB。在VMALLOC_START開始之前有一個8MB的洞,用於捕捉越界訪問。

內核通常把物理內存低於760MB的稱為線性映射內存(Normal Memory),而高於760MB以上的稱為高端內存(High Memory)。由於32位系統的尋址能力只有4GB,對於物理內存高於760MB而低於4GB的情況,我們可以從保留的240MB的虛擬地址空間中划出一部分用於動態映射高端內存,這樣內核就可以訪問到全部的4GB內存了。如果物理內存高於4GB,那么在ARMv7-A架構中就要使用LPE機制來擴展物理內存訪問了。用於映射高端內存的虛擬地址空間有限,所以又可以划分為兩部分,一部分為臨時映射區,另一部分為固定映射區, PKMAP指向的就是固定映射區。如下圖所示是ARM Vexpress平台上畫出內核空間的內存布局圖,詳細可以參考內核中文檔documentation/arm/memory.txt文件。

Linux內核ARM32內核內存布局圖

酷客網相關文章:


免責聲明!

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



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