内核地址空间布局

用户进程的虚拟地址空间由 mm_struct 描述。但内核虚拟地址空间的管理方式则完全不同——它没有一个统一的结构体像 mm_struct 那样去描述整个内核空间。

以下是Linux在ARM64处理器架构下的内核虚拟地址空间布局详解(区别于用户空间),结合内核源码与典型实现分析:


一、典型内核虚拟地址布局图


二、内核虚拟地址空间详细布局

1. 线性映射区 (Direct Mapping / Physmap)
  • 地址范围PAGE_OFFSET0xFFFF FFFF FFFF FFFF
  • 大小:内核虚拟地址空间长度的一半
  • 映射方式
// 物理地址 → 虚拟地址转换
virt = phys + PAGE_OFFSET - PHYS_OFFSET
  • 作用:建立物理内存的1:1线性映射,内核通过此区域直接访问物理内存。 把物理内存线性映射到一个连续的虚拟地址区,方便快速通过 virt_to_physphys_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_STARTPAGE_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_STARTPCI_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_STARTFIXADDR_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_STARTVMALLOC_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_VADDRMODULES_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_STARTMODULES_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)

参考资料

  1. Professional Linux Kernel Architecture,Wolfgang Mauerer
  2. Linux内核深度解析,余华兵
  3. Linux设备驱动开发详解,宋宝华
  4. linux kernel 4.12
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值