linux 虚拟地址空间区域划分
arm 虚拟地址空间划分
Documentation\arm\memory.rst
=============== =============== =============================================== Start End Use =============== =============== =============================================== ffff8000 ffffffff copy_user_page / clear_user_page use. For SA11xx and Xscale, this is used to setup a minicache mapping. ffff4000 ffffffff cache aliasing on ARMv6 and later CPUs. ffff1000 ffff7fff Reserved. Platforms must not use this address range. ffff0000 ffff0fff CPU vector page. The CPU vectors are mapped here if the CPU supports vector relocation (control register V bit.) fffe0000 fffeffff XScale cache flush area. This is used in proc-xscale.S to flush the whole data cache. (XScale does not have TCM.) fffe8000 fffeffff DTCM mapping area for platforms with DTCM mounted inside the CPU. fffe0000 fffe7fff ITCM mapping area for platforms with ITCM mounted inside the CPU. ffc80000 ffefffff Fixmap mapping region. Addresses provided by fix_to_virt() will be located here. ffc00000 ffc7ffff Guard region ff800000 ffbfffff Permanent, fixed read-only mapping of the firmware provided DT blob fee00000 feffffff Mapping of PCI I/O space. This is a static mapping within the vmalloc space. VMALLOC_START VMALLOC_END-1 vmalloc() / ioremap() space. Memory returned by vmalloc/ioremap will be dynamically placed in this region. Machine specific static mappings are also located here through iotable_init(). VMALLOC_START is based upon the value of the high_memory variable, and VMALLOC_END is equal to 0xff800000. PAGE_OFFSET high_memory-1 Kernel direct-mapped RAM region. This maps the platforms RAM, and typically maps all platform RAM in a 1:1 relationship. PKMAP_BASE PAGE_OFFSET-1 Permanent kernel mappings One way of mapping HIGHMEM pages into kernel space. MODULES_VADDR MODULES_END-1 Kernel module space Kernel modules inserted via insmod are placed here using dynamic mappings. TASK_SIZE MODULES_VADDR-1 KASAn shadow memory when KASan is in use. The range from MODULES_VADDR to the top of the memory is shadowed here with 1 bit per byte of memory. 00001000 TASK_SIZE-1 User space mappings Per-thread mappings are placed here via the mmap() system call. 00000000 00000fff CPU vector page / null pointer trap CPUs which do not support vector remapping place their vector page here. NULL pointer dereferences by both the kernel and user space are also caught via this mapping.
high_memory 值的确定
PAGE_OFFSET ~ high_memory-1 是属于线性映射区,也叫 low memory 区,high_memory 的值由以下函数确定
由 adjust_lowmem_bounds() 可知,high_memory 的确定和 VMALLOC 区有关
#define VMALLOC_OFFSET (8*1024*1024) #define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) #define VMALLOC_END 0xff800000UL
特别说明:
物理内存也是按区划分,有低端内存(对应虚拟内存区域的线性映射区)、高端内存(可被映射到虚拟内存区域的vmalloc来使用)
x86虚拟地址空间划分
linux通过宏“PAGE_OFFSET”将4GB的虚拟地址空间(32bit平台)划分成内核地址空间和进程地址空间两部分。“PAGE_OFFSET”的值支持通过Kconfig配置,其默认的值是“0xC0000000”。下面以经典的“PAGE_OFFSET=0xC0000000”来看下linux对虚拟地址空间的详细划分(不同于上面的arm32)。
Linux将虚拟地址空间划分为:0~3G为用户空间,3~4G为内核空间。下面是一个经典划分图:
下面简要介绍下各个部分意义:
【1】Process address space
0x0000 0000 ~ PAGE_OFFSET - 1 :用户进程虚拟地址空间,用于用户空间地址映射。每个进程独有,进程通过mmap()系统调用建立的映射都在这个区域内(其实这个区间高地址还包含内核模块区域,范围为MODULES_VADDR ~ MODULES_END-1)。
【2】Physical memory mapping
PAGE_OFFSET ~ high_memory - 1 :物理内存映射区,又名“Kernel direct-mapped RAM region”,即内核直接物理内存映射区域,也叫线性空间。这个区域用于将内核空间前896M和物理空间的前896M进行直接映射。
high_memory是指超过能直接映射的物理内存部分,超过“high_memory”分界线的虚拟地址属于高端内存区域,“高端”是相对直接映射区的“low memory”而定的。引入高端内存映射这样一个概念的主要原因就是我们所安装的内存大于1G时,内核的1G线性地址空间无法建立一个完全的直接映射来触及整个物理内存空间,而对于80x86开启PAE的情况下,允许的最大物理内存可达到64G,因此内核将自己的最后128M的线性地址空间腾出来,用以完成对高端内存的暂时性映射。而在64位的系统上就不存在这样的问题了,因为可用的线性地址空间远大于可安装的内存。
该区域的物理页面是内核能够直接使用的,比如内核程序中代码段、全局变量以及kmalloc获取的堆内存等。从此处获取内存一般是连续的,但是不能太大。kmalloc申请的是直接线性映射的连续物理内存,因为是直接映射不需要建立页表,所以效率较高,缺点是只能分配小内存,最大只有整个线性空间大小(896M)
内核中有相关的宏来实现线性映射区虚拟地址到物理地址的查找过程,例如__pa(x)和__va(x)
用于映射高端物理内存的虚拟地址空间划分,因平台而异,上图给出的是x86中一种经典划分,下面分析下高端内存映射区域是如何使用的。
【3】Vmalloc area
VMALLOC_START ~ VMALLOC_END :区域由vmalloc ioremap使用,物理不连续,是内核动态映射区域,用于动态映射物理内存。此区域由vmalloc函数执行动态地址分配,虚拟地址连续但物理地址不连续的内存。物理地址一般是HIGH_MEM区域,但也可以是其他物理地址,比如外设寄存器地址。Vmalloc 区域和直接映射区有一个8MB的隔离区,另外每个独立的Vmalloc area中间都有4KB的隔离区,主要是为了“捕获”对内存的越界访问(因为空洞未映射,访问会报错,以达到捕捉越界行为的目的)。
【4】persistent kernel map
PKMAP_START ~ FIXADDR_START:区域是kmap 内核永久映射空间。这里的永久并不是指调用kmap()建立的映射关系会一直持续下去无法解除,而是指在调用kunmap()解除映射之前这种映射会一直存在,这是相对于临时内核映射机制而言的。另外kmap申请不到会导致阻塞。
【5】fix-map area
FIXADDR_START ~ FIXADDR_END :区域是kmap_atomic 使用的内核固定映射空间,又称临时映射区,固定映射区不是线性转换,而是强制指定的任意映射,每个固定的线性地址都映射到一块物理内存页。以任意方式映射任意物理地址。(ARM中有3MB空间)。临时是因为会强制映射,覆盖以前已经有的映射,所以这种映射不会阻塞,可以用在中断处理流程中。主要用于使用线性地址常量代替指针变量,提高内存访问效率。
IMX6ULL 配置如下
CONFIG_VMSPLIT_2G=y
所以 PAGE_OFFSET 是 0x80000000,即内核空间的起始虚拟地址是 0x80000000。
PAGE_OFFSET: the virtual address of the start of the kernel image // 内核固件存储的起始地址是 PAGE_OFFSET
由于 IMX6ULL DDR 内存所在的物理地址是 0x80000000,内核固件存储在 DDR 首地址,所以内核空间的物理地址等于虚拟地址。
链接脚本设置的起始链接地址就是 0x80008000,因为在内核启动时执行自解压完成后,会跳转到解压后的地址处 0x80008000 运行