上次从系统的启动过程了解内存预分配的一些情况,现在从Linux内存管理机制memblock内核代码及CMA机制再看看RHEL/Rocky/AlmaLinux8内存管理的一些情况。
1.了解memblock预留内存
mmeblock是内存的一种管理机制,主要管理这两种内存:一种是系统可用部分的物理内存(usable),也就是/proc/meminfo里看到的总内存都是提供给系统使用的;另一种是用户预留部分的内存(reserved),用户自己特殊使用,这部分在系统总内存里看不到;reserved主要包括静态内存(内存中的某些部分使永久分配给内核的,例如代码段和数据段,ramdisk和dtb占用的空间等,是系统内存的一部分,不能被侵占,也不参与内存的分配。)和预留内存(GPU/camera/多核共享的内存都需要预留大量连续内存,这部分内存平时不使用,但是必须为各个应用场景预留。)。
memblock 结构体维护着上述两种内存, 其中成员 memory 维护着可用物理内存区域;成员 reserved 维护预留的内存区域。
memblock的算法实现是,它将所有的状态都保持在一个全局变量__initdata_memblock中,算法的初始化以及内存的申请释放都是在将内存块的状态做变更。那么从数据结构入手,__initdata_memblock是一个memblock结构体,其定义如下:
# cat /usr/src/kernels/4.18.0-425.19.2.el8_7.x86_64/include/linux/memblock.h
...
struct memblock {
bool bottom_up; /* is bottom up direction? */
phys_addr_t current_limit;
struct memblock_type memory;
struct memblock_type reserved;
#ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAP
struct memblock_type physmem;
#endif
};
...
定义 含义
bottom_up 表示分配器的分配方式,true表示从低地址向高地址分配,false则相反
current_limit 来表示用来限制alloc的内存申请
memory 表示usabe内存区域
reserved 表示保留reserved内存区域
memory和reserved是很关键的一个数据结构,memblock算法的内存初始化和申请释放都是围绕着他们展开工作。
physmem 表示所有物理内存
往下看看memory和reserved的结构体memblock_type定义:
...
struct memblock_region {
phys_addr_t base;
phys_addr_t size;
enum memblock_flags flags;
#ifdef CONFIG_NEED_MULTIPLE_NODES
int nid;
#endif
};
...
定义 含义
struct memblock_region-表示内存区域
base:表示区域的物理地址
size:表示区域大小
flags:表示内存区域属性
nid:表示NUMA(Non Uniform Memory Access,非一致内存访问架构)节点id
...
struct memblock_type {
unsigned long cnt; /* number of regions */
unsigned long max; /* size of the allocated array */
phys_addr_t total_size; /* size of all regions */
struct memblock_region *regions;
};
...
定义 含义
struct memblock_type:表示特定类型的内存区域的集合
cnt:表示当前状态(memory/reserved)的内存区域可用数
max:表示可支持的最大区域数
total_size:表示当前状态(memory/reserved)的空间大小,也就是内存总大小空间
regions:表示用于保存内存块信息的结构(包括基址、大小和标记等)
name:表示内存类型符号名称
memblock 方式预留内存需要有内核代码,就不深入研究了;
2.通过限时可用内存增加预留内存
1)修改可用内存
# cp -p /etc/default/grub /etc/default/grub.20230417
# vi /etc/default/grub
限制可用内存为512M,物理内存是1G。
# diff /etc/default/grub /etc/default/grub.20230417
6c6
< GRUB_CMDLINE_LINUX="crashkernel=0 mem=512M resume=/dev/mapper/almalinux-swap rd.lvm.lv=almalinux/root rd.lvm.lv=almalinux/swap"
---
> GRUB_CMDLINE_LINUX="crashkernel=0 resume=/dev/mapper/almalinux-swap rd.lvm.lv=almalinux/root rd.lvm.lv=almalinux/swap"
# grub2-mkconfig -o /boot/grub2/grub.cfg
# reboot
2)确认可用内存正好为512M
# cat /proc/iomem |grep RAM
00001000-0009ebff : System RAM # 4096~650239+1 646,144
00100000-1fffffff : System RAM # 1048576~536870911+1= 535,822,336
# dmesg |grep e820 |grep usable
[ 0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009ebff] usable # ①
[ 0.000000] BIOS-e820: [mem 0x0000000000100000-0x000000003fedffff] usable
[ 0.000000] BIOS-e820: [mem 0x000000003ff00000-0x000000003fffffff] usable # 1048576~1073741823+1=1,072,693,248
[ 0.000000] e820: remove [mem 0x20000000-0xfffffffffffffffe] usable
#减少可用内存
[ 0.000000] e820: update [mem 0x00000000-0x00000fff] usable ==> reserved # ② ①-②=00001000-0009ebff 646,144
[ 0.000000] e820: remove [mem 0x000a0000-0x000fffff] usable
#减少可用内存
3.CMA连续内存分配方式预留内存
1)什么是CMA?
CMA,Contiguous Memory Allocator,是内存管理子系统中的一个模块,负责物理地址连续的内存分配。一般系统会在启动过程中,从整个memory中配置一段连续内存用于CMA,然后内核其他的模块可以通过CMA的接口API进行连续内存的分配。CMA的核心并不是设计精巧的算法来管理地址连续的内存块,实际上它的底层还是依赖内核伙伴系统这样的内存管理机制,或者说CMA是处于需要连续内存块的其他内核模块(例如DMA mapping framework)和内存管理模块之间的一个中间层模块,主要功能包括:
1、解析DTS或者命令行中的参数,确定CMA内存的区域,这样的区域我们定义为CMA area。
2、提供cma_alloc和cma_release两个接口函数用于分配和释放CMA pages
3、记录和跟踪CMA area中各个pages的状态
4、调用伙伴系统接口,进行真正的内存分配。
2)内核中为何建立CMA模块?
Linux内核中已经提供了各种内存分配的接口,为何还有建立CMA这种连续内存分配的机制呢?
先来看看内核哪些模块有物理地址连续的需求。huge page模块需要物理地址连续是显而易见的。大家都熟悉的处理器(不要太古老),例如ARM64,其内存管理单元都可以支持多个页面大小(4k、64K、2M或者更大的page size),但在大多数CPU架构上,Linux内核总是倾向使用最小的page size,即4K page size。Page size大于4K的page统称为“huge page”。对于一个2M的huge page,MMU会把一个连续的2M的虚拟地址mapping到连续的、2M的物理地址上去,当然,这2M size的物理地址段必须是由512个地址连续的4k page frame组成。
当然,更多的连续内存的分配需求来自形形色色的驱动。例如现在大家的手机都有视频功能,camer功能,这类驱动都需要非常大块的内存,而且有DMA用来进行外设和大块内存之间的数据交换。对于嵌入式设备,一般不会有IOMMU,而且DMA也不具备scatter-getter功能,这时候,驱动分配的大块内存(DMA buffer)必须是物理地址连续的。
3)CMA的状况确认
CMA是2010年三星公司Michal Nazarewicz 首先提出,用于用于解决连续物理内存申请问题。Linux Kernel 3.5中首次合并改功能。
RHEL/Rocky/AlmaLinux8中都具备该功能
# uname -a
Linux test 4.18.0-425.19.2.el8_7.x86_64 #1 SMP Tue Apr 4 05:30:47 EDT 2023 x86_64 x86_64 x86_64 GNU/Linux
# dmesg |grep -i cma
[ 0.000000] Memory: 261120K/523892K available (12293K kernel code, 5878K rwdata, 8376K rodata, 2552K init, 14256K bss, 117240K reserved, 0K cma-reserved)
# find / -name cma.h
/usr/src/kernels/4.18.0-425.19.2.el8_7.x86_64/include/linux/cma.h
/usr/src/kernels/4.18.0-425.19.2.el8_7.x86_64/include/trace/events/cma.h
RHEL/Rocky/AlmaLinux7中未启用该功能
# uname -a
Linux chefserver 3.10.0-693.11.6.el7.x86_64 #1 SMP Thu Jan 4 01:06:37 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
# dmesg |grep -i cma
# find / -name cma.h
/usr/src/kernels/3.10.0-693.el7.x86_64/include/config/cma.h
/usr/src/kernels/3.10.0-693.11.6.el7.x86_64/include/config/cma.h
至此,对Linux内存管理增加了解了一点皮毛。
参考URL:
https://blog.csdn.net/u012294613/article/details/124133143
https://zhuanlan.zhihu.com/p/554043638
https://blog.csdn.net/ludaoyi88/article/details/115280238
https://www.bbsmax.com/A/A7zg8VRlJ4/
https://www.yii666.com/blog/356096.html
https://blog.csdn.net/u012489236/article/details/105348579/
网友评论