用户进程的虚拟地址空间由 mm_struct 描述。但内核虚拟地址空间的管理方式则完全不同——它没有一个统一的结构体像 mm_struct 那样去描述整个内核空间。
以下是Linux在ARM64处理器架构下的内核虚拟地址空间布局详解(区别于用户空间),结合内核源码与典型实现分析:
一、典型内核虚拟地址布局图

二、内核虚拟地址空间详细布局
1. 线性映射区 (Direct Mapping / Physmap)
- 地址范围:
PAGE_OFFSET到0xFFFF FFFF FFFF FFFF - 大小:内核虚拟地址空间长度的一半
- 映射方式:
// 物理地址 → 虚拟地址转换
virt = phys + PAGE_OFFSET - PHYS_OFFSET
- 作用:建立物理内存的1:1线性映射,内核通过此区域直接访问物理内存。 把物理内存线性映射到一个连续的虚拟地址区,方便快速通过
virt_to_phys、phys_to_virt转换;适合 DMA、内存管理等
/*
* PAGE_OFFSET - the virtual address of the start of the linear map (top
* (VA_BITS - 1))
* KIMAGE_VADDR - the virtual address of the start of the kernel image
* VA_BITS - the maximum number of bits for virtual addresses.
* VA_START - the first kernel virtual address.
* TASK_SIZE - the maximum size of a user space task.
* TASK_UNMAPPED_BASE - the lower boundary of the mmap VM area.
*/
#define VA_BITS (CONFIG_ARM64_VA_BITS)
#define VA_START (UL(0xffffffffffffffff) << VA_BITS)
#define PAGE_OFFSET (UL(0xffffffffffffffff) << (VA_BITS - 1))
#define KIMAGE_VADDR (MODULES_END)
#define MODULES_END (MODULES_VADDR + MODULES_VSIZE)
#define MODULES_VADDR (VA_START + KASAN_SHADOW_SIZE)
#define MODULES_VSIZE (SZ_128M)
#define VMEMMAP_START (PAGE_OFFSET - VMEMMAP_SIZE)
#define PCI_IO_END (VMEMMAP_START - SZ_2M)
#define PCI_IO_START (PCI_IO_END - PCI_IO_SIZE)
#define FIXADDR_TOP (PCI_IO_START - SZ_2M)
#define TASK_SIZE_64 (UL(1) << VA_BITS)
#ifdef CONFIG_COMPAT
#define TASK_SIZE_32 UL(0x100000000)
#define TASK_SIZE (test_thread_flag(TIF_32BIT) ? \
TASK_SIZE_32 : TASK_SIZE_64)
#define TASK_SIZE_OF(tsk) (test_tsk_thread_flag(tsk, TIF_32BIT) ? \
TASK_SIZE_32 : TASK_SIZE_64)
#else
#define TASK_SIZE TASK_SIZE_64
#endif /* CONFIG_COMPAT */
#define TASK_UNMAPPED_BASE (PAGE_ALIGN(TASK_SIZE / 4))
#define KERNEL_START _text
#define KERNEL_END _end
/*
* The size of the KASAN shadow region. This should be 1/8th of the
* size of the entire kernel virtual address space.
*/
#ifdef CONFIG_KASAN
#define KASAN_SHADOW_SIZE (UL(1) << (VA_BITS - 3))
#else
#define KASAN_SHADOW_SIZE (0)
#endif
2. vmemmap 区域
- 地址范围:
VMEMMAP_START到PAGE_OFFSET - 作用:存放所有物理页的
struct page(物理页的元数据)结构体 - 计算:
// 物理页帧号 (pfn) → struct page 虚拟地址
#define __vmemmap ((struct page *)VMEMMAP_START)
struct page *page = __vmemmap + pfn;
/*
* VMEMMAP_SIZE - allows the whole linear region to be covered by
* a struct page array
*/
#define VMEMMAP_SIZE (UL(1) << (VA_BITS - PAGE_SHIFT - 1 + STRUCT_PAGE_MAX_SHIFT))
#define VMEMMAP_START (PAGE_OFFSET - VMEMMAP_SIZE)
3. PCI I/O 区域
- 地址范围:
PCI_IO_START到PCI_IO_END - 大小:16MB
- 用途:映射PCI(Peripheral Component Interconnect)设备的I/O空间
/*
* Size of the PCI I/O space. This must remain a power of two so that
* IO_SPACE_LIMIT acts as a mask for the low bits of I/O addresses.
*/
#define PCI_IO_SIZE SZ_16M
#define PCI_IO_END (VMEMMAP_START - SZ_2M)
#define PCI_IO_START (PCI_IO_END - PCI_IO_SIZE)
4. 固定映射区 (Fixed Mappings)
- 地址范围:从
FIXADDR_START到FIXADDR_TOP - 映射类型:
// arch/arm64/include/asm/fixmap.h
enum fixed_addresses {
FIX_HOLE, // 保留洞
FIX_EARLYCON, // 早期串口
FIX_FDT, // 设备树
__end_of_fixed_addresses
};
#define FIXADDR_SIZE (__end_of_permanent_fixed_addresses << PAGE_SHIFT)
#define FIXADDR_START (FIXADDR_TOP - FIXADDR_SIZE)
- 作用:内核启动早期(内存管理未初始化)访问关键硬件资源
#define FIXADDR_TOP (PCI_IO_START - SZ_2M)
5. vmalloc 动态映射区
- 地址范围:
VMALLOC_START到VMALLOC_END - 用途:
- 分配物理不连续的大内存块(如内核模块)
- 实现
**ioremap**(),用于设备寄存器映射 - 内存不足时的后备分配区
- 内核镜像在 vmalloc 区域,起始虚拟地址是(KIMAGE_VADDR + TEXT_OFFSET) ,其中 KIMAGE_VADDR 是内核镜像的虚拟地址的基准值,等于内核模块区域的结束地址 MODULES_END;TEXT_OFFSET 是内存中的内核镜像相对内存起始位置的偏移。
/*
* VMALLOC range.
*
* VMALLOC_START: beginning of the kernel vmalloc space
* VMALLOC_END: extends to the available space below vmmemmap, PCI I/O space
* and fixed mappings
*/
#define VMALLOC_START (MODULES_END)
#define VMALLOC_END (PAGE_OFFSET - PUD_SIZE - VMEMMAP_SIZE - SZ_64K)
6. 模块区域 (Module Area)
- 地址范围:
MODULES_VADDR到MODULES_END - 大小:128M
- 用途:加载内核模块的代码和数据(
*.ko)
#define MODULES_END (MODULES_VADDR + MODULES_VSIZE)
#define MODULES_VADDR (VA_START + KASAN_SHADOW_SIZE)
#define MODULES_VSIZE (SZ_128M)
7. KASAN 影子内存区
- 地址范围:
VA_START到MODULES_VADDR - 大小:内核虚拟地址空间长度的 1/8
- 作用:存储内存访问的元数据(检测越界/释放后使用等错误)。内核地址消毒剂(Kernel Address SANitizer,KASAN)是一个动态的内存错误检查工 具。它为发现释放后使用和越界访问这两类缺陷提供了快速和综合的解决方案。
/*
* The size of the KASAN shadow region. This should be 1/8th of the
* size of the entire kernel virtual address space.
*/
#ifdef CONFIG_KASAN
#define KASAN_SHADOW_SIZE (UL(1) << (VA_BITS - 3))
#else
#define KASAN_SHADOW_SIZE (0)
#endif
三、内核空间查询方法
物理地址空间布局
通过/proc/iomem查询物理内存空间布局,它是一个伪文件,由内核动态生成,位于 /proc 文件系统中。它展示了系统中所有已注册的物理内存区域(device tree是它的一个重要信息来源),包括:
- RAM 区域,如下图所示
- 设备寄存器映射区域(如 PCI、USB、GPU)
- 固件保留区域(如 EFI、ACPI)
- 内核使用区域(如页表、内核代码)
- 驱动映射区域(如
xhci-hcd,dwc3,eth0等)

查看内核符号表
/boot/System.map-$(uname -r),是编译符号表。
/proc/kallsyms ( kernel all symbols )是 Linux 内核提供的一个非常强大的调试接口,它列出了所有内核符号的地址和类型,包括函数、变量、内核模块符号等。它是内核开发、调试、性能分析、漏洞研究等场景中的核心工具之一。
内容是当前内核符号表的快照。每一行表示一个符号的地址、类型和名称。


输出格式解析
| 字段 | 说明 |
|---|---|
| 地址 | 符号在内核虚拟地址空间中的地址 |
| 类型 | 符号类型(见下表) |
| 名称 | 符号名称(函数、变量等) |
符号类型说明
| 类型 | 含义 |
|---|---|
T | 全局函数,定义在内核文本段(text) |
t | 局部函数,定义在内核文本段 |
D | 全局变量,定义在数据段(data) |
d | 局部变量,定义在数据段 |
B | 全局变量,定义在 BSS 段(未初始化数据) |
b | 局部变量,定义在 BSS 段 |
R | 只读数据段(rodata) |
r | 局部只读数据 |
A | 异常表项(exception table) |
a | 局部异常表项 |
N | 调试符号(如 DWARF) |
n | 局部调试符号 |
U | 未定义符号(通常是模块依赖) |
vmalloc区
/proc/vmallocinfo 是 Linux 内核提供的一个调试接口,用于显示当前系统中通过 vmalloc() 分配的虚拟内存区域的详细信息。它主要用于分析内核空间的虚拟地址使用情况,尤其是 VMALLOC 区域的碎片、分配失败等问题。

格式说明
<start_addr>-<end_addr> <size> <caller> pages=<n> vmalloc [vpages]
| 字段 | 含义 |
|---|---|
start_addr - end_addr | 虚拟地址范围 |
size | 分配的字节数 |
caller | 调用者函数及偏移 |
pages=n | 分配的页数(通常为 4KB 页) |
vmalloc | 分配类型(也可能是 ioremap, vmap等) |
vpages | 表示使用了 vmalloc_to_page()映射页表(通常用于模块代码段) |
模块区(Kernel Modules)
通过/proc/modules 查询当前系统中已加载的所有内核模块。

格式说明
<module_name> <size> <refcount> <dependencies> Live <address> [flags]
| 字段 | 含义 |
|---|---|
module_name | 模块名称 |
size | 模块占用内存大小(字节) |
refcount | 引用计数(被其他模块使用的次数) |
dependencies | 依赖的其他模块 |
Live | 表示模块正在运行 |
address | 加载到内核的地址(通常为 vmalloc地址) |
flags | 如 (OE) 表示模块是自定义编译的(Out-of-tree) |
参考资料
- Professional Linux Kernel Architecture,Wolfgang Mauerer
- Linux内核深度解析,余华兵
- Linux设备驱动开发详解,宋宝华
- linux kernel 4.12

623

被折叠的 条评论
为什么被折叠?



