linux内存分配器类型,内核早期内存分配器:memblock

原标题:内核早期内存分配器:memblock

本文转载自Linux爱好者

本文来自 程雪涛的自荐投稿

Linux内核使用伙伴系统管理内存,那么在伙伴系统工作前,如何管理内存?答案是memblock。

memblock在系统启动阶段进行简单的内存管理,记录物理内存的使用情况。

在进一步介绍memblock之前,有必要先了解下系统内存的使用情况:

首先,内存中的某些部分是永久的分配给内核的,比如内核代码段和数据段,ramdisk和fdt占用的空间等,它们是系统内存的一部分,但是不能被侵占,也不参与内存分配,称之为静态内存;

其次,GPU,Camera等都需要预留大量连续内存,这部分内存平时不用,但是系统必须提前预留好,称之为预留内存;

最后,内存的其余部分称之为动态内存,是需要内核管理的宝贵资源。

memblock把物理内存划分为若干内存区,按使用类型分别放在memory和reserved两个集合(数组)中,memory即动态内存的集合,reserved集合包括静态内存和预留内存。

1. memblock关键数据结构

memblock数据结构定义如下:

bd2fa1ce2988864f20fe1c62eb1526b9.png

memblock相关数据结构十分的简单,内核还为memblock定义了一个全局变量,并为其赋初值,如下:

996a9301f5aca7eedc58cfff4dabf4f0.png

memory类型的内存集合指向memblock_memory_init_regions数组,最多可以记录128个内存区。

reserved类型的内存集合指向memblock_reserved_init_regions数组,最多可以记录128个内存区。

注:内核代码经常用到类似”__initdata_memblock”的宏定义,通常用来指定变量或函数所在的section,该宏的定义如下:

#define __meminitdata __attribute__ ((__section__(".meminit.data")))

2. memblock基本操作1) 添加内存区

f8661c13ebbe7646b3335548e559ffa0.png

分别为memory和reserved集合添加内存区,如果新加入的内存区与原有内存区重叠,则合并到原有内存区,否则插入新内存区。

实际工作由memblock_add_range()完成,type参数指定内存集合类型。

需要注意的是该函数内部会执行两次:

第一次计算需要插入几个内存区,如果超过允许的最大内存区个数,则double内存区数组;

第二次执行内存区的实际插入与合并操作。

29219e50ba117c119fc38720525e598e.png

ed50e5fe4da2d082eb4c98395c44119b.png

2) 移除内存区

intmemblock_remove(phys_addr_tbase,phys_addr_tsize);

从memory集合移除给定物理地址所指的内存区,如果是内存区域的一部分,则涉及到调整region大小,或者将一个region拆分成两个region。

系统将不会为移除的内存区建立内存映射,这部分内存区后续应该由DMA或CMA管理。

3) 分配内存

phys_addr_t memblock_alloc(phys_addr_tsize,phys_addr_talign);

phys_addr_t memblock_alloc_range(phys_addr_tsize,phys_addr_talign,phys_addr_tstart,phys_addr_tend);

使用该函数向kernel申请一块可用的物理内存,memblock使用自顶向下(取决于bottom_up的值)的方式查找空闲内存,实际操作是在memory region中查找合适的内存,并加入到reserved region中以标记这块内存已经被使用。

4) 释放内存

intmemblock_free(phys_addr_tbase,phys_addr_tsize);

使用该函数来释放由memblock_alloc申请到的物理内存。

3. 探测系统可用内存

内核是如何知晓物理内存的拓扑结构呢?相信很多人都有过类似的疑问。

通过DDR的模式寄存器(MR8),可以很容易获得内存密度,进而推断出内存容量,这部分工作通常由bootloader完成,然后使用fdt或者atag等方式传递给内核。

以fdt为例,内核解析memory节点,取得物理内存的拓扑结构(起始地址及大小),并添加到memblock中,代码如下:

setup_arch()->setup_machine_fdt()->early_init_dt_scan()->early_init_dt_scan_memory()

{

......

reg=of_get_flat_dt_prop(node,"reg",&l);

......

while((endp-reg)>=(dt_root_addr_cells+dt_root_size_cells)){

u64base,size;

base=dt_mem_next_cell(dt_root_addr_cells,&reg);

size=dt_mem_next_cell(dt_root_size_cells,&reg);

......

early_init_dt_add_memory_arch(base,size);

}

}

该函数扫描memory节点,并解析reg属性,注意此时DeviceTree还没有执行unflattern操作,需要使用”fdt”类型接口解析dtb。

以4G DDR为例,输出的调试信息如下:

[0.000000]memory scan nodememory,regsize32,data:080080,100000000557e

[0.000000]-80000000,80000000

[0.000000]-100000000,7e550000

reg属性由addr和size组成,分别占用2个cell(u32类型数据),上面的reg data可以看成:“0 00000080 0 00000080, 01000000 0 0 00557e”。

dtb使用big endian方式存储数据,需要转换成cpu字节序。

解析出来的内存包含两个Rank,起始地址分别是0x80000000和0x100000000,这是系统的可用内存,用来初始化memory region。

voidearly_init_dt_add_memory_arch(base,size)

{

constu64phys_offset=__pa(PAGE_OFFSET);

......

if(base

pr_warning("Ignoring memory range 0x%llx - 0x%llxn",

base,phys_offset);

size-=phys_offset-base;

base=phys_offset;

}

memblock_add(base,size);

}

从fdt解析的内存信息是否可信呢?内核有自己的判断,在启动阶段,内核会根据自身的运行地址计算内存基地址,即PHYS_OFFSET。

如果base地址小于phys_offset,则内核使用可信的phys_offset做为主存的基地址。

这里要注意区分PHYS_OFFSET, PAGE_OFFSET:

PAGE_OFFSET是内核虚拟地址空间的起始地址,PHYS_OFFSET是RAM在物理空间的起始地址,内核空间的地址映射通常具有固定的偏移量,即:

#define __virt_to_phys(x) (((phys_addr_t)(x) - PAGE_OFFSET + PHYS_OFFSET))

4. 记录系统预留内存

这里说的系统预留内存,包括静态内存(内核Image,ramdisk,fdt等占用空间),以及系统为Camera,Display等子系统预留的大量连续内存。

另外,高通平台通常包含多核,还需要为Modem,TZ/TA等预留运行空间,这部分空间类似静态内存,都是永久分配给其它核心使用,根据节点属性,通常由DMA管理。

arm64_memblock_init()函数初始化系统预留内存,代码如下:

7fabaa17fd1131d675d7af5b3ece018e.png

“no-map”属性决定向reserved region添加内存区,还是从memory region移除内存区,二者差别在于内核不会给”no-map”属性的内存区建立内存映射,即该内存区不在动态内存管理范围。

预留内存还会被添加到reserved_mem数组,为后续的初始化做准备,”reg”属性指定内存区的起始地址和大小,如果没有”reg”属性,还需要为内存区分配空间。

1fc777bdea8b6d99539092a143ae98c0.png

至此,memblock的初始化工作已经基本完成了,主要是记录系统内存的使用情况:

memory region记录系统了所有可用的动态内存;

reserved region记录了系统预留内存,这部分内存通常由CMA管理,也属于动态内存范畴;

reserved_mem数组则记录系统所有预留内存,包括”no-map”属性的内存区,为后续进一步初始化工作做准备。

5. 初始化预留内存区

内存向来是系统的宝贵资源,预留内存如果仅做为子系统的专用内存,就有点浪费了。

Linux内核引入CMA(Contiguous Memory Allocator,连续内存分配器)。

其工作原理是:为驱动预留一段内存,当驱动不用时,Memory Allocator(Buddy System)可以分配给用户进程使用;而当驱动需要使用时,就将进程占用的内存通过回收或者迁移的方式腾出来,供驱动使用。

但是并不是所有的预留内存都由CMA管理,像Modem,TA等永久分配给其它核心使用的内存空间,内核并不为这部分空间建立内存映射,而是交由DMA管理。

通过上面的分析,我们看到所有预留内存信息都记录在reserved_mem数组,下面先看看该结构体的定义:

1d8aa1a1a85f955060dd255bd06f4840.png

“reg”和”no-map”属性前面介绍过,详细可以参考reserved-memory.txt,”compatible”属性使用标准定义,内核注册两种不同的处理方法:

RESERVEDMEM_OF_DECLARE(dma,"removed-dma-pool",removed_dma_setup);

RESERVEDMEM_OF_DECLARE(cma,"shared-dma-pool",rmem_cma_setup);

“removed-dma-pool”表示该内存区位于DMA管理区,内核不可见(没有页表)。

“shared-dma-pool”表示该内存区位于CMA管理区,平时是可用的,只有需要时才分配给驱动使用。

如果没有”reg”属性,即没有指定预留内存的起始地址,则需要由系统分配预留内存,然后初始化reserved_mem的ops成员:

011fb83f8a08110e9834c8b5c39a3d7b.png

此处为”shared-dma-pool”类型的内存注册操作方法,cma_init_reserved_mem初始化cma_area(CMA管理区)。

reserved_mem的ops成员包括init和release两个操作方法:

structreserved_mem_ops{

int(*device_init)(structreserved_mem *rmem,structdevice *dev);

void(*device_release)(structreserved_mem *rmem,structdevice *dev);

};

驱动注册预留内存区时调用device_init方法,为设备指定预留内存操作方法(DMA)或预留内存区域(CMA),这些方法包括预留内存的申请,释放和mmap等。

6. 连续内存分配器(CMA)

CMA的初始化必须在buddy系统工作之前和memblock分配器初始化完成之后。

在ARM中,初始化CMA的接口是:

90ab91aca8c098144e293414b11d0b1b.png

以命令行参数”cma=32M@0-0xfffffff”为例: size_cmdline = 32M, base_cmdline = 0x0, limit_cmdline = 0xffffffff 。

计算好CMA的size等值以后就进入cma_declare_contiguous中:

e0fef2ff5f5be21e4e61f5dea3860e73.png

177aff4851126f4f9a951a73ef194337.png

以上只是将CMA区域预留下来,并记录到相关数组,进一步初始化和使用需要等slab等子系统初始化完成后了。

CMA并不直接开放给驱动开发人员,在注册设备时可以使用”memory-region”属性指定要操作的内存区域,需要分配DMA内存时,调用DMA相关函数就可以了。

例如dma_alloc_coherent(),最终DMA相关的分配函数会到达CMA的分配函数dma_alloc_from_contiguous()。

7. 进阶阅读

memblock和bootmem的区别

CMA(连续内存分配器)

Contiguous Memory Allocator (CMA) 源码分析

Linux内核最新连续内存分配器(CMA) — 避免预留大块内存

内存管理笔记(CMA)返回搜狐,查看更多

责任编辑:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/527547.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java substring截取字符串_java基础教程之字符串的介绍,比较重要的一个知识点【下】...

字符串操作1、获取子字符串通过String类的substring()方法可对字符串进行截取。这些方法的共同点就是都是利用字符串的下标进行截取。应明确字符串下标是从0开始的。substring()方法被两种不同的方法重载,来满足不同的需要。(1)substring(intbeginIndex)该方法返回的…

在linux环境下安装wiringpi库,wiringPi库的pwm配置及使用说明

本文介绍树莓派(raspberry pi)在linux c 环境下的硬件pwm配置及使用方法。1. 下载安装wiringPi此步骤建议参考官网指南,wiringPi提供了对树莓派的硬件IO访问,包括GPIO/I2C/PWM等,下载安装后本地会出现wiringPi文件夹,根目录下有/e…

excel split函数_Excel 字符串拆分

用 Excel 处理数据时,有时需要对字符串进行拆分。对于比较简单的拆分,使用 Excel 函数可以顺利完成,但碰到一些特殊需求,或者拆分的规则比较复杂时,则很难用 Excel 实现了。这里列出一些拆分需求示例,分析拆…

python优雅编程_Python优雅地可视化数据

[导读]声明:由于本文的代码大部分是参考书中的例子,所以不提供完整代码,只提供示例片段,也就是只能看出某一部分用法,感兴趣的需要在自己的数据上学习测试。 声明:由于本文的代码大部分是参考书中的例子&am…

sparkstreaming 读取mysql_第十篇|SparkStreaming手动维护Kafka Offset的几种方式

Spark Streaming No Receivers 方式的createDirectStream 方法不使用接收器,而是创建输入流直接从Kafka 集群节点拉取消息。输入流保证每个消息从Kafka 集群拉取以后只完全转换一次,保证语义一致性。但是当作业发生故障或重启时,要保障从当前…

anaconda 安装pytorch_conda上安装PyTorch

conda上安装PyTorch这里的安装系统:Windows系统第一步,打开Anaconda Prompt第二步,为pytorch创建一个环境:conda create -n pytorch python3.8(这里的pytorch是环境的名称,python3.8表示安装的是3.8版本的p…

curd什么意思中文_查英英字典:What a shame是什么意思?

查英英字典:What a shame是什么意思?“互联网”时代学习英语有两种“学习方法”:简单粗暴学法和自己动手丰衣足食法。一、简单粗暴法:直接问老师要“中文答案”在过去非互联网时代,“老师”往往是提供“答案”的唯一渠…

c++读出像素矩阵_Python传numpy矩阵调c++(求3D图像连通区域)

Python有很多种调c的方法,有的复杂有的简单,有时使用的时候反而不知道到底该用哪一种比较好,其实没有最好的方法,只有适合不适合自己。本文从我所遇到的问题说起,然后讲述另一种比较简单的python调c并且传参numpy矩阵的…

android四大组件的作用简书,Android四大组件是什么

Android四大组件是:活动、服务、广播接收器、内容提供商。它们的英文名称是ACTIVITY、SERVICE、BroadcastReceiver、Content Provider。四个组件分别起到不同的作用,相互配合才能确保安卓系统的正常运行,因此是缺一不可的。Android四大组件及…

python 获取文件大小_第41p,超级重要,Python中的os库

大家好,我是杨数Tos,这是《从零基础到大神》系列课程的第41篇文章,第二阶段的课程:Python基础知识:Python内置库之os库的使用。学习本课程,建议先看一遍:【计算机基础知识】课程。os模块是与操作…

惠普打印机节能环保认证证书_低成本高效办公 苏宁惠普超品日这几款打印机了解下!...

【PConline 导购】说到打印机,很多朋友会想到公司那台不停运转的打印机。其实,伴随着近几年来打印机技术的成熟,其打印成本也一降再降,这就让有打印的需求的中下型企业,甚至个人,都会去选购一款合适的打印产…

vm客户机隔离不能选_开汽车美容店,这些位置绝对不能选,会让你门可罗雀,生意惨淡...

之前的文章里,讲了一些开汽车美容店选址的要领,今天,来聊聊一些更加细致的选址要素,让你避免错误选址而导致生意不佳。门面宽度小于2.5米的不要选一辆普通汽车的宽度在1.8米-2米之间,加上两侧后视镜各20公分左右&#…

markdown格式_第1篇:如何将Markdown笔记转入ANKI复习? | 学习骇客

用技术和心理学改善学习 第128次摘要:将日常使用的Markdown笔记软件与复习工具ANKI结合起来,于ANKI用户而言可以简化学习过程,于一般的学习者而言可以解决笔记“记而不学”的问题。本文摘选自视频课程《复习的技术,跟LEO学ANKI》(…

android手机无分区无法刷机,手机刷死了别说没提醒!安卓设备刷机前必看

大家好,清明节已经过去了,上班的感觉是不是很不爽?但是告诉大家一个好消息是:本周只需要煎熬三天,大家就又可以休息了!听了这个消息,不爽的心情是不是稍微好一些了?本期的微信和大家…

mysqlbinlog工具_带你解析MySQL binlog

前言:我们都知道,binlog可以说是MySQL中比较重要的日志了,在日常学习及运维过程中,也经常会遇到。不清楚你对binlog了解多少呢?本篇文章将从binlog作用、binlog相关参数、解析binlog内容三个方面带你了解binlog。1.bin…

inputstream 初始化_如何完美回答面试官问的Mybatis初始化原理!

前言对于任何框架而言,在使用前都要进行一系列的初始化,MyBatis也不例外。本章将通过以下几点详细介绍MyBatis的初始化过程。MyBatis的初始化做了什么MyBatis基于XML配置文件创建Configuration对象的过程手动加载XML配置文件创建Configuration对象完成初…

html中加减号怎么输入,jQuery 实现点击加减号改变input标签中的value值,该怎么解决...

jQuery 实现点击加减号改变input标签中的value值我想点击左右两边的加减号,让中间input标签中的value属性值做出相应的改变,jQuery怎么实现?------解决思路----------------------$("button1").click(function(){var num $("…

java mybatis狂神说sql_狂神说MyBatis01:第一个程序

狂神说MyBatis系列连载课程,通俗易懂,基于MyBatis3.5.2版本,欢迎各位狂粉转发关注学习,视频同步文档。未经作者授权,禁止转载MyBatis简介环境说明:jdk 8 MySQL 5.7.19maven-3.6.1IDEA学习前需要掌握&#x…

鸿蒙系统暗黑2,暗黑破坏神2为什么被称为神作!看看装备强化系统就知道有多完美...

暗黑破坏神2之所以被玩家们称为神作是因为真的好玩,那么游戏的精髓到底在哪呢?个人觉得还要算其出色的装备强化系统,如果应用在现在的部分作品中,暗黑破坏神2的特色可以总结为肝,彻底肝。但它又区别于传统的必须肝&…

c语言api_用C语言来拓展python的功能

python是一门功能强大的高级脚本语言,它的强大不仅表现在其自身的功能上,而且还表现在其良好的可扩展性上,正因如此,python已经开始受到越来越多人的青睐,并且被屡屡成功地应用于各类大型软件系统的开发过程中。与其它…