Linux内存管理:虚拟地址空间(AArch64)
相關背景:
文章開始前,先聊聊相關的背景知識,我們知道64位處理器的虛擬地址已經支持到了64bit,但是64位處理器的物理地址總線實際位寬并沒有達到64bit,常用的地址線寬有39bit和48bit,最新的ARMv8.2架構也已經可以支持到52bit了。那為什么沒有支持到64bit呢?以常用的48bit地址線寬舉例,其最大尋址能力是2^48 bytes(即256TB內存),對于當今的個人電腦或服務器來說都是足夠用的。再加上增加地址總線的寬度會給芯片設計上帶來不小的難度,所以并沒有一步到位搞成64bit。
本文主要介紹ARM64位處理器地址空間的布局。前文已提到地址總線寬度有39bit、48bit以及52bit,且64位處理器又支持3級或4級頁表,頁大小也可以配置成4KB或64KB,組合起來的話情況太多,本文為了簡化,就基于最常用組合展開敘述:48bit地址總線,4級頁表,頁面大小4KB。 且不包括ARM64虛擬化模式和安全模式下的情況。
新內核變動:
再來說一下寫這篇文章時的感慨吧,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 +0100
arm64: mm: Flip kernel VA space
In order to allow for a KASAN shadow that changes size at boot time, one
must fix the KASAN_SHADOW_END for both 48 & 52-bit VAs and "grow" the
start address. Also, it is highly desirable to maintain the same
function addresses in the kernel .text between VA sizes. Both of these
requirements 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 the
kernel VA range and everything else in the higher ranges.
所以本文基于目前mainline的內核版本v5.9-rc2 展開敘述。
虛擬地址空間:
各體系架構處理器的虛擬地址空間的布局各不相同,下面是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]
注:以上layout來自內核文檔Documentation/arm64/memory.rst。x86的位于Documentation/x86/x86_64/mm.txt
為了直觀點,畫了幅圖:
地址空間的定義:
內核中劃分的這么多區域,且都有自己對應的地址與大小,這些地址和大小在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。
from:https://zhuanlan.zhihu.com/p/207001939?utm_source=wechat_timeline
總結
以上是生活随笔為你收集整理的Linux内存管理:虚拟地址空间(AArch64)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 双卡已落伍 三卡三待全键盘手机亮相
- 下一篇: 一个简易的键盘按键测试程序