Linux - 内存 - memblock 分配器

2023-12-13 21:28

本文主要是介绍Linux - 内存 - memblock 分配器,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

说明

  • memblock是Linux内核启动早期用于管理物理内存的机制,在伙伴系统(Buddy System)接管内存管理之前为系统提供物理内存分配、释放等功能。
  • 相对于伙伴系统,memblock功能和实现较为简单。
  • 本文基于:linux_5.10 arm64平台。

历史

  • 启动早期的内存管理器实现有bootmem和memblock,bootmem是早期内核采用,4.x以后内核内核采用memblock,配置了NO_BOOTMEM宏。
  • memblock取代了bootmem算法。

实现原理

获取物理内存布局

  • 不同于伙伴系统以内存页为操作对象,memblock以物理内存段为操作对象,系统从dtb或者uboot传递来的mem信息中解析出总的物理内存信息(核心信息是地址范围),此时的物理内存是一段段的地址空间,再初始化memblock。

fdt方式

  • dts配置中有memory的配置
* 单段物理内存
memory {device_type = "memory";reg = <0x0 0x80000000 0x0 0x10000000>;
};
* 多段物理内存
memory@0 {device_type = "memory";reg = <0x00000000 0x00000000 0x00000000 0x05e00000>,<0x00000000 0x05f00000 0x00000000 0x00001000>,<0x00000000 0x05f02000 0x00000000 0x00efd000>,<0x00000000 0x06e00000 0x00000000 0x0060f000>,<0x00000000 0x07410000 0x00000000 0x1aaf0000>,<0x00000000 0x22000000 0x00000000 0x1c000000>;
};

uboot bootargs方式

  • uboot启动linux时,可以通过linux的启动参数bootargs,传递物理内存信息(基址和size),初始化memblock。
  • 格式如下:
mem=size@start
  • 配置流程
* 函数调用栈
early_mem  //file: arch/arm64/mm/init.c,解析出物理内存信息,保存在全局变量中
arm64_memblock_init //file: arch/arm64/mm/init.c 
->  memblock_add
* 核心代码
void __init arm64_memblock_init(void)
{...if (memory_limit != PHYS_ADDR_MAX) { //全局变量非默认值memblock_mem_limit_remove_map(memory_limit); memblock_add(__pa_symbol(_text), (u64)(_end - _text));}...
}

可用段查找原理

  • memblock内存分配时可用段查找采用first match算法,即占用首先找到的可以段。
  • 内存分配查找的方向可以是从高到低,也可以是从低到高,通过总context中的成员变量bottom_up决定。

两个阶段

  • memblock有两个阶段
  1. memblock init之前;主要是静态分配,根据dts配置中预留内存定义(reserved memory),内核本身(code等),dtb等,在物理内存上分配出所需的预留内存。
  2. memblock init之后,伙伴系统初始化完之前;主要是Linux内核机制产生的动态内存分配。
  • 两个阶段以以memblock configuration为分隔。

分配结果

  • memblock分配结果都是预留内存,分配结束后固定占用,无法释放和复用。

代码逻辑

  • memblock源码在Linux内核根目录下的:
include/linux/memblock.h 
mm/memblock.c 

数据结构和实例

  • memblock从大到小定义了三个数据结构,如下:
  1. 总context定义
struct memblock {bool bottom_up;  //内存分配的方向:从高到低(FALSE)、从低到高(TRUE)phys_addr_t current_limit; //最大内存地址struct memblock_type memory; //可管理的内存段struct memblock_type reserved; //预留内存
};
  1. 内存类型定义
struct memblock_type {unsigned long cnt; //内存区域个数(占用数组个数)unsigned long max; //最大区域个数(数组总个数)phys_addr_t total_size; //该内存类型总大小struct memblock_region *regions; //包含的内存区域数组char *name;
};
  1. 内存区域(地址段)定义
struct memblock_region {phys_addr_t base;  //基址phys_addr_t size;  //空间大小enum memblock_flags flags; //flag
#ifdef CONFIG_NUMAint nid;   //物理内存 node id,NUMA可以存在多个物理内存节点(node)
#endif
};enum memblock_flags {MEMBLOCK_NONE           = 0x0,  /* No special request */ //正常MEMBLOCK_HOTPLUG        = 0x1,  /* hotpluggable region */ //可插拔区域MEMBLOCK_MIRROR         = 0x2,  /* mirrored region */MEMBLOCK_NOMAP          = 0x4,  /* don't add to kernel direct mapping */ //no map区域
};
  • 总context实例,以全局静态变量(保存在BSS段中)形式定义,区域都是预先定义的全局静态数组,数组个数默认128(INIT_MEMBLOCK_REGIONS)。
static struct memblock_region memblock_memory_init_regions[INIT_MEMBLOCK_REGIONS] __initdata_memblock;
static struct memblock_region memblock_reserved_init_regions[INIT_MEMBLOCK_RESERVED_REGIONS] __initdata_memblock;
#ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAP
static struct memblock_region memblock_physmem_init_regions[INIT_PHYSMEM_REGIONS];
#endifstruct memblock memblock __initdata_memblock = {.memory.regions         = memblock_memory_init_regions,.memory.cnt             = 1,    /* empty dummy entry */.memory.max             = INIT_MEMBLOCK_REGIONS,.memory.name            = "memory",.reserved.regions       = memblock_reserved_init_regions,.reserved.cnt           = 1,    /* empty dummy entry */.reserved.max           = INIT_MEMBLOCK_RESERVED_REGIONS,.reserved.name          = "reserved",.bottom_up              = false,.current_limit          = MEMBLOCK_ALLOC_ANYWHERE,
};

API

  1. memblock_add
  • 将内存区域加入memblock可管理的内存区域,即memory的region队列。
  1. memblock_free
  • 将一个物理内存段从预留内存中移除,该内存段重新标记为可用。
int memblock_free(phys_addr_t base, phys_addr_t size)
{memblock_dbg("   memblock_free: [%#016llx-%#016llx] %pF\n",(unsigned long long)base,(unsigned long long)base + size - 1,(void *)_RET_IP_);kmemleak_free_part_phys(base, size);return memblock_remove_range(&memblock.reserved, base, size);
}

调试

  1. 获取memblock的详细分配log,可以通过在uboot bootargs中加入“memblock=debug”,内核启动后,通过dmesg或者/proc/kmsg查看调试信息。
  2. linux kernel启动后可以通过debug fs查看内存地址范围和reserved区域,如下:
/sys/kernel/debug/memblock/memory #交由系统管理的内存 
/sys/kernel/debug/memblock/reserved #预留的内存
* 需要开启配置
CONFIG_DEBUG_FS
CONFIG_ARCH_KEEP_MEMBLOCK //是否保留memblock分配信息
  • 该功能不是很有必要并且会占用一定物理资源,方法1足以满足调试需求,新内核CONFIG_ARCH_KEEP_MEMBLOCK配置默认是关的。

交接

  • buddy分配器初始化ok后,memblock分配器将内存管理工作交接给buddy(伙伴)分配器。

标志

  • memblock和伙伴系统的交接标志:释放init进程内存(free_initmem函数处理),之后系统可用内存(/proc/meminfo中的MemTotal)就固定了。

分配实例分析

  • 分配实例

这篇关于Linux - 内存 - memblock 分配器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/490013

相关文章

防止Linux rm命令误操作的多场景防护方案与实践

《防止Linuxrm命令误操作的多场景防护方案与实践》在Linux系统中,rm命令是删除文件和目录的高效工具,但一旦误操作,如执行rm-rf/或rm-rf/*,极易导致系统数据灾难,本文针对不同场景... 目录引言理解 rm 命令及误操作风险rm 命令基础常见误操作案例防护方案使用 rm编程 别名及安全删除

Linux下MySQL数据库定时备份脚本与Crontab配置教学

《Linux下MySQL数据库定时备份脚本与Crontab配置教学》在生产环境中,数据库是核心资产之一,定期备份数据库可以有效防止意外数据丢失,本文将分享一份MySQL定时备份脚本,并讲解如何通过cr... 目录备份脚本详解脚本功能说明授权与可执行权限使用 Crontab 定时执行编辑 Crontab添加定

使用docker搭建嵌入式Linux开发环境

《使用docker搭建嵌入式Linux开发环境》本文主要介绍了使用docker搭建嵌入式Linux开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录1、前言2、安装docker3、编写容器管理脚本4、创建容器1、前言在日常开发全志、rk等不同

Redis实现高效内存管理的示例代码

《Redis实现高效内存管理的示例代码》Redis内存管理是其核心功能之一,为了高效地利用内存,Redis采用了多种技术和策略,如优化的数据结构、内存分配策略、内存回收、数据压缩等,下面就来详细的介绍... 目录1. 内存分配策略jemalloc 的使用2. 数据压缩和编码ziplist示例代码3. 优化的

linux系统上安装JDK8全过程

《linux系统上安装JDK8全过程》文章介绍安装JDK的必要性及Linux下JDK8的安装步骤,包括卸载旧版本、下载解压、配置环境变量等,强调开发需JDK,运行可选JRE,现JDK已集成JRE... 目录为什么要安装jdk?1.查看linux系统是否有自带的jdk:2.下载jdk压缩包2.解压3.配置环境

深入解析C++ 中std::map内存管理

《深入解析C++中std::map内存管理》文章详解C++std::map内存管理,指出clear()仅删除元素可能不释放底层内存,建议用swap()与空map交换以彻底释放,针对指针类型需手动de... 目录1️、基本清空std::map2️、使用 swap 彻底释放内存3️、map 中存储指针类型的对象

Linux搭建ftp服务器的步骤

《Linux搭建ftp服务器的步骤》本文给大家分享Linux搭建ftp服务器的步骤,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录ftp搭建1:下载vsftpd工具2:下载客户端工具3:进入配置文件目录vsftpd.conf配置文件4:

Python内存优化的实战技巧分享

《Python内存优化的实战技巧分享》Python作为一门解释型语言,虽然在开发效率上有着显著优势,但在执行效率方面往往被诟病,然而,通过合理的内存优化策略,我们可以让Python程序的运行速度提升3... 目录前言python内存管理机制引用计数机制垃圾回收机制内存泄漏的常见原因1. 循环引用2. 全局变

Linux实现查看某一端口是否开放

《Linux实现查看某一端口是否开放》文章介绍了三种检查端口6379是否开放的方法:通过lsof查看进程占用,用netstat区分TCP/UDP监听状态,以及用telnet测试远程连接可达性... 目录1、使用lsof 命令来查看端口是否开放2、使用netstat 命令来查看端口是否开放3、使用telnet

Linux系统管理与进程任务管理方式

《Linux系统管理与进程任务管理方式》本文系统讲解Linux管理核心技能,涵盖引导流程、服务控制(Systemd与GRUB2)、进程管理(前台/后台运行、工具使用)、计划任务(at/cron)及常用... 目录引言一、linux系统引导过程与服务控制1.1 系统引导的五个关键阶段1.2 GRUB2的进化优