Linux内存管理之理论基础

本文详细介绍了内存管理中的分段和分页机制,阐述了它们在地址空间映射和保护方面的作用。分段机制将程序划分为多个段,而分页机制则以固定大小的页面进行内存分配。在地址转换过程中,ARM32和ARM64处理器使用页表进行虚拟地址到物理地址的映射。此外,还探讨了Linux内存管理的数据结构以及ARM32和ARM64的内存结构差异。
摘要由CSDN通过智能技术生成

1、分段机制

分段(Segmentation)机制的基本思想,是把程序所需的内存空间的虚拟地址映射到某个物理地址空间中,分段机制可以解决地址空间保护问题。如果一个进程访问了没有映射的虚拟地址空间,或者访问了不属于该进程的虚拟地址空间,那么CPU会捕捉这个越界访问,并且拒绝该次访问。同时CPU会发送一个异常操作给操作系统,由操作系统处理这些异常,这就是常说的缺页异常。分段机制就是把程序分成了若干段,包括text、data、BSS、stack和heap,其中data存放的是已初始化的全局变量(这里包括初始化了的静态变量),BSS存放的是未初始化的静态变量,text存放的是代码段。
分段机制的粒度比较大,是以进程为单位,这样内存的使用率很低,也就是说,在物理内存不够的情况下,通常做法就是将进程所有段置换到磁盘空间,这样会导致大量块设备IO操作,从而影响整个系统性能。

2、分页机制

分页机制是将内存分配的粒度固定到以页面为大小,进程的虚拟地址空间也按照页面来分割,这样常用的数据和代码就可以以页面为单位驻留到内存中,而那些不常用的页面可以交换到磁盘中,当然后续再使用这些页面时可以通过页缓存(Page Cache)的方式提高对置换到磁盘中的数据的访问,这样能够做到节省物理内存(这里需要注意的是,如果页缓存过大,同样会浪费物理内存)。再MMU中,进程以页面为单位,将虚拟内存通过CPU硬件单元映射到物理内存中,这样物理内存也是以页面为单位进行管理,物理页面又称为页帧(Page frame),每个物理页面都有一个编号称为页帧号(Page frame Number,PFN),当前内核版本支持三种页面大小:4KB、16KB和64KB,分别对应的内核配置(以ARM64为例)是CONFIG_ARM64_4K_PAGES、CONFIG_ARM64_16K_PAGES和CONFIG_ARM64_64K_PAGES,在物理内存很大(如服务器级别的内存多数以TB为单位)的情况下,页面大小越小,内存管理成本越高,产生的性能缺陷越多,这个时候需要处理器对巨/大页的支持,如2M、1GB的巨页。
对于不同的处理器,MMU硬件单元的组成不同,ARM64的MMU包括了TLB和页表遍历单元两个部件。

3、VirtAddr与PhyAddr的转换

当TLB未命中时,32位处理器查询页表的过程如下图
在这里插入图片描述

处理器以虚拟地址的Bit[32:20]作为索引值,在一级页表中找到页表项。
一级页表的表项存放的是二级页表表项的物理基地址,处理器以虚拟地址的Bit[19:12]作为索引值,在二级页表中找到相应的页表项;
二级页表的页表项中存放的是单个页面大小(如4KB)的物理基地址,以此基地址加上虚拟地址的页内偏移量Bit[11:0],最后得到的就是最终的物理地址。
这个过程就是页表查询与翻译。
arm64处理器的页表查询过程如下图所示:
在这里插入图片描述
具体页表查询和翻译过程为:
1、处理器根据页表基地址控制寄存器和虚拟地址来判断使用哪个页表基地址寄存器,页表基地址寄存器有两种,分别是TTBR0和TTBR1。当虚拟地址的第63位为1时选择TTBR1,当虚拟地址的第63位为0时选择TTBR0。这两个页表基地址寄存器的作用于32位arm的页表基地址一样,用于存放1级页表(L0页表)的基地址。
2、处理器分别将虚拟地址的47:39(简称VA[47:39])作为L0页表索引,将VA[38:30]作为L1页表的索引,将VA[29:20]作为L2页表的索引,将VA[19:12]作为L3页表的索引,将VA[11:0]作为页面地址偏移。
3、每个页表都有512个页表项,4级页表项里面存放的是页面地址的基地址,L0存放的是L1的基地址,L1存放的是L2的基地址,L2存放的是L3的基地址。物理地址由L3页表存放的物理基地址加上VA[11:0]存放的地址偏移构成。

4、数据结构

在这里插入图片描述
Linux内存管理区可分为以下几种:
ZONE_DMA:用于ISA设备的DMA操作,范围是0~10MB,只适用于x86架构,arm架构无该内存管理区

5、ARM32和ARM64内存结构区分

ARM32内存结构

arm32总共有4G虚拟内存寻址空间,其中高地址位内核空间,地址范围为3G ~ 4G ( 0xBFFFFFFF~0xFFFFFFFF),低地址为用户空间,地址范围0 ~ 3G(0x00000000 ~ 0xC0000000)

ARM64内存结构

arm64不同于arm32,arm64内存大小几乎不受限制, 总共分为三段,低地址的用户空间,中间部分的非规范区域,以及高地址的内核空间,用户空间和内核空间地址范围由内核配置CONFIG_ARM64_VA_BITS以及页面大小CONFIG_ARM64_XK_PAGES(X有4/16和64三个值)决定。
CONFIG_ARM64_XK_PAGES决定了当前系统单个页面大小。
CONFIG_ARM64_VA_BITS决定了PAGE_OFFSET、VA_START、TASK_SIZE等值的大小,同时影响了用户内存和内核内存的地址范围,表示的是虚拟地址空间大小,CONFIG_ARM64_VA_BITS配置的值依赖于页面内存大小,对于4K页面,该配置只有两种39-bit和48-bit,对于16K页面,该配置有47-bit和48-bit两种,对于64K页面,该配置支持42-bit、48-bit和52-bit三种值。下面介绍CONFIG_ARM64_VA_BITS如何换算虚拟内存地址空间:

#define VMEMMAP_SHIFT	(PAGE_SHIFT - STRUCT_PAGE_MAX_SHIFT)
#define VMEMMAP_SIZE	((_PAGE_END(VA_BITS_MIN) - PAGE_OFFSET) >> VMEMMAP_SHIFT)

/*
 * PAGE_OFFSET - the virtual address of the start of the linear map, at the
 *               start of the TTBR1 address space.
 * PAGE_END - the end of the linear map, where all other kernel mappings begin.
 * KIMAGE_VADDR - the virtual address of the start of the kernel image.
 * VA_BITS - the maximum number of bits for virtual addresses.
 */
#define VA_BITS			(CONFIG_ARM64_VA_BITS)
#define _PAGE_OFFSET(va)	(-(UL(1) << (va)))
#define PAGE_OFFSET		(_PAGE_OFFSET(VA_BITS))
#define KIMAGE_VADDR		(MODULES_END)
#define BPF_JIT_REGION_START	(_PAGE_END(VA_BITS_MIN))
#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)
#define MODULES_VADDR		(BPF_JIT_REGION_END)
#define MODULES_VSIZE		(SZ_128M)
#define VMEMMAP_START		(-(UL(1) << (VA_BITS - VMEMMAP_SHIFT)))
#define VMEMMAP_END		(VMEMMAP_START + VMEMMAP_SIZE)
#define PCI_IO_END		(VMEMMAP_START - SZ_8M)
#define PCI_IO_START		(PCI_IO_END - PCI_IO_SIZE)
#define FIXADDR_TOP		(VMEMMAP_START - SZ_32M)

#if VA_BITS > 48
#define VA_BITS_MIN		(48)
#else
#define VA_BITS_MIN		(VA_BITS)
#endif

#define _PAGE_END(va)		(-(UL(1) << ((va) - 1)))

#define KERNEL_START		_text
#define KERNEL_END		_end

/*
 * Generic and tag-based KASAN require 1/8th and 1/16th of the kernel virtual
 * address space for the shadow region respectively. They can bloat the stack
 * significantly, so double the (minimum) stack size when they are in use.
 */
#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
#define KASAN_SHADOW_OFFSET	_AC(CONFIG_KASAN_SHADOW_OFFSET, UL)
#define KASAN_SHADOW_END	((UL(1) << (64 - KASAN_SHADOW_SCALE_SHIFT)) \
					+ KASAN_SHADOW_OFFSET)
#define PAGE_END		(KASAN_SHADOW_END - (1UL << (vabits_actual - KASAN_SHADOW_SCALE_SHIFT)))
#define KASAN_THREAD_SHIFT	1
#else
#define KASAN_THREAD_SHIFT	0
#define PAGE_END		(_PAGE_END(VA_BITS_MIN))
#endif /* CONFIG_KASAN */

#define MIN_THREAD_SHIFT	(14 + KASAN_THREAD_SHIFT)

/*
 * VMAP'd stacks are allocated at page granularity, so we must ensure that such
 * stacks are a multiple of page size.
 */
#if defined(CONFIG_VMAP_STACK) && (MIN_THREAD_SHIFT < PAGE_SHIFT)
#define THREAD_SHIFT		PAGE_SHIFT
#else
#define THREAD_SHIFT		MIN_THREAD_SHIFT
#endif

#if THREAD_SHIFT >= PAGE_SHIFT
#define THREAD_SIZE_ORDER	(THREAD_SHIFT - PAGE_SHIFT)
#endif

#define THREAD_SIZE		(UL(1) << THREAD_SHIFT)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值