一、新內核變動
? ?????????kernel變化的真快,之前我記得4.x的內核的內核空間的線性映射區位于內核空間的高地址處的128TB,且當前的博客和一些書籍也都還是這樣介紹。可翻了翻kernel的Documentation/arm64/memory.rst文檔,發現最新的kernel已將這128TB移到了內核空間的最低地址處了。具體是2019年8月的一個commit,如下:
commit 14c127c957c1c6070647c171e72f06e0db275ebf
Author: Steve Capper <steve.capper@arm.com>
Date: Wed Aug 7 16:55:14 2019 +0100arm64: mm: Flip kernel VA spaceIn order to allow for a KASAN shadow that changes size at boot time, onemust fix the KASAN_SHADOW_END for both 48 & 52-bit VAs and "grow" thestart address. Also, it is highly desirable to maintain the samefunction addresses in the kernel .text between VA sizes. Both of theserequirements necessitate us to flip the kernel address space halves s.t.the direct linear map occupies the lower addresses.This patch puts the direct linear map in the lower addresses of thekernel VA range and everything else in the higher ranges.
?二、虛擬地址空間
內核?4.x? 和 5.0 版本的虛擬地址空間分布:
新arm64 內存分布:我的內核版本是5.15
各體系架構處理器的虛擬地址空間的布局各不相同,下面是ARM64位處理器使用48位虛擬地址,4級頁表,頁面大小4KB時的layout:
Start End Size Use
-----------------------------------------------------------------------
0000000000000000 0000ffffffffffff 256TB user
ffff000000000000 ffff7fffffffffff 128TB kernel logical memory map
ffff800000000000 ffff9fffffffffff 32TB kasan shadow region
ffffa00000000000 ffffa00007ffffff 128MB bpf jit region
ffffa00008000000 ffffa0000fffffff 128MB modules
ffffa00010000000 fffffdffbffeffff ~93TB vmalloc
fffffdffbfff0000 fffffdfffe5f8fff ~998MB [guard region]
fffffdfffe5f9000 fffffdfffe9fffff 4124KB fixed mappings
fffffdfffea00000 fffffdfffebfffff 2MB [guard region]
fffffdfffec00000 fffffdffffbfffff 16MB PCI I/O space
fffffdffffc00000 fffffdffffdfffff 2MB [guard region]
fffffdffffe00000 ffffffffffdfffff 2TB vmemmap
ffffffffffe00000 ffffffffffffffff 2MB [guard region]
地址空間的定義:
內核中劃分的這么多區域,且都有自己對應的地址與大小,這些地址和大小在kernel中哪里定義著呢?具體位于:arch/arm64/include/asm/memory.h。
#define PAGE_OFFSET (_PAGE_OFFSET(VA_BITS))
#define KIMAGE_VADDR (MODULES_END)
#define BPF_JIT_REGION_START (KASAN_SHADOW_END)
#define BPF_JIT_REGION_SIZE (SZ_128M)
#define BPF_JIT_REGION_END (BPF_JIT_REGION_START + BPF_JIT_REGION_SIZE)
#define MODULES_END (MODULES_VADDR + MODULES_VSIZE)
.....
- PAGE_OFFSET
內核線性映射區的起始地址,大小為128TB。 - KASAN_SHADOW_START
KASAN影子內存的起始虛擬地址,大小為32TB。為什么是32TB呢?因為KASAN通常使用1:8或1:16比例的內存來做影子內存,分別對應大小為256TB/8=32TB或256TB/16=16TB,這里表示的是1:8的情況所以是32TB。 - KIMAGE_VADDR
定義了內核鏡像的鏈接地址,通過其定義"#define KIMAGE_VADDR (MODULES_END)"看出它整好位于modules區域的結尾處,即vmalloc區域的起始地址。vmlinux.ld.S文件設置鏈接地址時會用到它,start_kernel->paging_init->map_kernel會將內核鏡像的各個段依次映射到該區域。 - VMALLOC_START
定義了vmalloc區域的起始地址,大小約等于93TB。記得之前ARM32可以通過bootargs去控制vmalloc區域的大小,不知道64還有沒。但是有沒有也沒所謂了,畢竟64位的處理器上虛擬地址空間已不像32位處理器那么緊張。 - VMEMMAP_START
定義了vmemmap區域的起始地址,大小2TB。sparsemem內存模型中用來存放所有struct page的虛擬地址空間。
寄存器TTBR0和TTBR1:
本文講到了內核地址空間和用戶地址空間,這就不得不提一下ARM64相關的兩個寄存器TTBR0和TTBR1。它們的功能類似于X86里的CR3寄存器用來存放進程的1級頁表(PGD)的基地址。但不同的是ARM64使用了兩個寄存器分別存放用戶空間和內核空間的1級頁表基地址。
我們知道所有進程的內核地址空間的頁表是共用一套的,所以TTBR1中的內容不會改變,永遠等于init_mm->swapper_pg_dir。但各個進程的用戶空間的頁表各自獨立,那么TTBR0中的內容則等于各自進程的task_struct->mm_struct->pgd
最后提一下,處理器如何知道什么時候訪問TTBR0,什么時候訪問TTBR1呢?ARMv8手冊中有提到,當CPU訪問地址時,若地址的第63bit為1則自動使用TTBR1,為0則使用TTBR0。
備注: 各個版本的虛擬地址分布, 有一點差異,但是大致區域是一致。比如不同的地方:
上面的知乎網友的圖, vmalloc_start 的地址是 0xffffa00000000000; 而我打印的? vmalloc_start 的地址是 0xffff800010000000;