Linux內核在啟動時會列印出內核記憶體空間的布局圖,下面是ARM Vexpress平臺列印出來的記憶體空間布局圖:

這部分資訊列印是在mem_init()函式中實作的,
[start_kernel->mm_init->mem_init]
pr_notice("Virtual kernel memory layout:\n"
" vmalloc : 0x%16lx - 0x%16lx (%6ld GB)\n"
#ifdef CONFIG_SPARSEMEM_VMEMMAP
" vmemmap : 0x%16lx - 0x%16lx (%6ld GB maximum)\n"
" 0x%16lx - 0x%16lx (%6ld MB actual)\n"
#endif
" fixed : 0x%16lx - 0x%16lx (%6ld KB)\n"
" PCI I/O : 0x%16lx - 0x%16lx (%6ld MB)\n"
" modules : 0x%16lx - 0x%16lx (%6ld MB)\n"
" memory : 0x%16lx - 0x%16lx (%6ld MB)\n"
" .init : 0x%p" " - 0x%p" " (%6ld KB)\n"
" .text : 0x%p" " - 0x%p" " (%6ld KB)\n"
" .data : 0x%p" " - 0x%p" " (%6ld KB)\n",
MLG(VMALLOC_START, VMALLOC_END),
#ifdef CONFIG_SPARSEMEM_VMEMMAP
MLG((unsigned long)vmemmap,
(unsigned long)vmemmap + VMEMMAP_SIZE),
MLM((unsigned long)virt_to_page(PAGE_OFFSET),
(unsigned long)virt_to_page(high_memory)),
#endif
MLK(FIXADDR_START, FIXADDR_TOP),
MLM(PCI_IO_START, PCI_IO_END),
MLM(MODULES_VADDR, MODULES_END),
MLM(PAGE_OFFSET, (unsigned long)high_memory),
MLK_ROUNDUP(__init_begin, __init_end),
MLK_ROUNDUP(_text, _etext),
MLK_ROUNDUP(_sdata, _edata));
編譯器在編譯目標檔案并且鏈接完成之后,就可以知道內核映像檔案最終的大小,接下來打包成二進制檔案,該操作由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檔案,查詢這個檔案可以找到這些地址的具體數值,

內核使用虛擬地址從MODULES_VADDR到MODULES_END這段14MB大小的記憶體區域,
#define MODULES_VADDR (PAGE_OFFSET - SZ_16M)
#ifdef CONFIG_HIGHMEM
#define MODULES_END (PAGE_OFFSET - PMD_SIZE)
#else
#define MODULES_END (PAGE_OFFSET)
#endif
用戶空間和內核空間使用3:1的劃分方法時,內核空間只有1GB大小,這1GB的映射空間,其中有一部分用于直接映射物理地址,這個區域稱為線性映射區,在ARM32平臺上,物理地址[0:760MB]的這一部分記憶體被線性映射到[3GB:3GB+768MB]的虛擬地址上,線性映射區的虛擬地址和物理地址相差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;
}
其中,__pa()把線性映射區的虛擬地址轉換為物理地址,轉換公式很簡單,即用虛擬地址減去PAGE_OFFSET(3GB),然后再加上PHYS_OFFSET(這個值在有的ARM平臺上為0,在ARM Vexpress平臺該值為0x6000_0000),
那么高端記憶體的起始地址(760MB)如何確定呢?
在內核初始化記憶體時,在santiy_check_meminfo()函式中確定高端記憶體的起始地址,全域變數high_memory來存放高端記憶體的起始地址,
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;
}
vmalloc_min計算出來的結果是0x2F80_0000,即760MB;
為什么內核只線性映射760MB呢?剩下的264MB的虛擬地址空間用來做什么呢?
那是保留給vmallc,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
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指向的就是固定映射區,如圖2.6所示是ARM Vexpress平臺上畫出內核空間的記憶體布局圖,詳細可以參考檔案documentation/arm/memory.txt檔案,

轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/3569.html
標籤:嵌入式
下一篇:QML設計飄散效果
