内核早期内存分配器:memblock
Linux内核使用伙伴系统管理内存,那么在伙伴系统工作前,如何管理内存?答案
是memblock。
memblock在系统启动阶段进行简单的内存管理,记录物理内存的使用情况。
在进一步介绍memblock之前,有必要先了解下系统内存的使用情况:
首先,内存中的某些部分是永久的分配给内核的,比如内核代码段和数据段,ramdisk和fdt占
用的空间等,它们是系统内存的一部分,但是不能被侵占,也不参与内存分配,称之为静态内存
;其次,GPU,Camera等都需要预留大量连续内存,这部分内存平时不用,但是系统必须提前预
留好,称之为预留内存;最后,内存的其余部分称之为动态内存,是需要内核管理的宝贵资源。
memblock把物理内存划分为若干内存区,按使用类型分别放在memory和reserved两个集
合(数组)中,memory即动态内存的集合,reserved集合包括静态内存和预留内存。
1. memblock关键数据结构
memblock数据结构定义如下:
memblock相关数据结构十分的简单,内核还为memblock定义了一个全局变量,并为其赋
初值,如下:
memory类型的内存集合指向memblock_memory_init_regions数组,最多可以记录128个内
存区。
reserved类型的内存集合指向memblock_reserved_init_regions数组,最多可以记录128个内
存区。
注:内核代码经常用到类似”__initdata_memblock”的宏定义,通常用来指定变量或函数所在
的section,该宏的定义如下:
2. memblock基本操作
1) 添加内存区
分别为memory和reserved集合添加内存区,如果新加入的内存区与原有内存区重叠,则合并
到原有内存区,否则插入新内存区。
实际工作由memblock_add_range()完成,type参数指定内存集合类型。
需要注意的是该函数内部会执行两次:
第一次计算需要插入几个内存区,如果超过允许的最大内存区个数,则double内存区数组;
第二次执行内存区的实际插入与合并操作。
2) 移除内存区
从memory集合移除给定物理地址所指的内存区,如果是内存区域的一部分,则涉及到调
更多精彩攻略访问gl.baidu.com 1
整region大小,或者将一个region拆分成两个region。
系统将不会为移除的内存区建立内存映射,这部分内存区后续应该由DMA或CMA管理。
3) 分配内存
使用该函数向kernel申请一块可用的物理内存,memblock使用自顶向下(取决于bottom_up
的值)的方式查找空闲内存,实际操作是在memory region中查找合适的内存,并加入到reserved
region中以标记这块内存已经被使用。
4) 释放内存
使用该函数来释放由memblock_alloc申请到的物理内存。
3. 探测系统可用内存
内核是如何知晓物理内存的拓扑结构呢?相信很多人都有过类似的疑问。
通过DDR的模式寄存器(MR8),可以很容易获得内存密度,进而推断出内存容量,这部分工
作通常由bootloader完成,然后使用fdt或者atag等方式传递给内核。
以fdt为例,内核解析memory节点,取得物理内存的拓扑结构(起始地址及大小),并添加
到memblock中,代码如下:
该函数扫描memory节点,并解析reg属性,注意此时DeviceTree还没有执行unflattern操作,
需要使用”fdt”类型接口解析dtb。
以4G DDR为例,输出的调试信息如下:
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。
从fdt解析的内存信息是否可信呢?内核有自己的判断,在启动阶段,内核会根据自身的运行
地址计算内存基地址,即PHYS_OFFSET。
如果base地址小于phys_offset,则内核使用可信的phys_offset做为主存的基地址。
这里要注意区分PHYS_OFFSET, PAGE_OFFSET:
PAGE_OFFSET是内核虚拟地址空间的起始地址,PHYS_OFFSET是RAM在物理空间的起
始地址,内核空间的地址映射通常具有固定的偏移量,即:
4. 记录系统预留内存
这里说的系统预留内存,包括静态内存(内核Image,ramdisk,fdt等占用空间),以及系统
为Camera,Display等子系统预留的大量连续内存。
更多精彩攻略访问gl.baidu.com 2
另外,高通平台通常包含多核,还需要为Modem,TZ/TA等预留运行空间,这部分空间类似
静态内存,都是永久分配给其它核心使用,根据节点属性,通常由DMA管理。
arm64_memblock_init()函数初始化系统预留内存,代码如下:
“no-map”属性决定向reserved region添加内存区,还是从memory region移除内存区,二者差
别在于内核不会给”no-map”属性的内存区建立内存映射,即该内存区不在动态内存管理范围。
预留内存还会被添加到reserved_mem数组,为后续的初始化做准备,”reg”属性指定内存区的
起始地址和大小,如果没有”reg”属性,还需要为内存区分配空间。
至此,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数组,下面先看看该结
构体的定义:
reserved-memory子节点包含预留内存属性,典型定义如下:
“reg”和”no-map”属性前面介绍过,详细可以参考reserved-memory.txt,”compatible”属性使用
标准定义,内核注册两种不同的处理方法:
“removed-dma-pool”表示该内存区位于DMA管理区,内核不可见(没有页表)。
“shared-dma-pool”表示该内存区位于CMA管理区,平时是可用的,只有需要时才分配给驱动
使用。
如果没有”reg”属性,即没有指定预留内存的起始地址,则需要由系统分配预留内存,然后初
始化reserved_mem的ops成员:
以”shared-dma-pool”为例,它的初始化函数如下:
更多精彩攻略访问gl.baidu.com 3
此处为”shared-dma-pool”类型的内存注册操作方法,cma_init_reserved_mem初始
化cma_area(CMA管理区)。
reserved_mem的ops成员包括init和release两个操作方法:
驱动注册预留内存区时调用device_init方法,为设备指定预留内存操作方法(DMA)或预留内存
区域(CMA),这些方法包括预留内存的申请,释放和mmap等。
6. 连续内存分配器(CMA)
CMA的初始化必须在buddy系统工作之前和memblock分配器初始化完成之后。 在ARM中,
初始化CMA的接口是:
@limit: 指定CMA区域的上限,在64位系统上@limit的值通常是0x100000000。
以命令行参数”cma=32M@0-0xfffffff”为例: size_cmdline = 32M, base_cmdline = 0x0,
limit_cmdline = 0xffffffff 。
计算好CMA的size等值以后就进入cma_declare_contiguous中:
dma_contiguous_default_are是用户自定义CMA管理区,定义如下:
下面进入cma_init_reserved_mem初始化用户自定义CMA管理区:
以上只是将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)
更多精彩攻略访问gl.baidu.com 4

内核早期内存分配器:memblock的更多相关文章

  1. 内存分配器memblock【转】

    转自:http://blog.csdn.net/kickxxx/article/details/54710243 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] 背景 Data ...

  2. 内核的bootmem内存分配器【转】

    转自:http://blog.csdn.net/zmxiangde_88/article/details/8041040 版权声明:本文为博主原创文章,未经博主允许不得转载. 在内核启动期间,伙伴系统 ...

  3. 理解 glibc malloc:主流用户态内存分配器实现原理

    https://blog.csdn.net/maokelong95/article/details/51989081 Understanding glibc malloc 修订日志: 2017-03- ...

  4. 内存分配器 (Memory Allocator)

    对于大多数开发人员而言,系统的内存分配就是一个黑盒子,就是几个API的调用.有你就给我,没有我就想别的办法. 来UC前,我就是这样觉得的.实际深入进去时,才发现这个领域里也是百家争鸣.非常热闹.有操作 ...

  5. Go内存分配器可视化指南【译】【精】

    当我第一次开始尝试理解 Go 语言的内存分配器时,整个过程让我抓狂.一切看起来都像一个神秘的黑盒子.因为几乎所有技术魔法(technical wizardry)都隐藏在抽象之下,所以你需要一层一层的剥 ...

  6. Linux内核笔记--内存管理之用户态进程内存分配

    内核版本:linux-2.6.11 Linux在加载一个可执行程序的时候做了种种复杂的工作,内存分配是其中非常重要的一环,作为一个linux程序员必然会想要知道这个过程到底是怎么样的,内核源码会告诉你 ...

  7. 24小时学通Linux内核之内存管理方式

    昨天分析的进程的代码让自己还在头昏目眩,脑子中这几天都是关于Linux内核的,对于自己出现的一些问题我会继续改正,希望和大家好好分享,共同进步.今天将会讲诉Linux如何追踪和管理用户空间进程的可用内 ...

  8. linux内核申请内存函数

    kmap函数:    把某块高端内存映射到页表,然后返回给用户一个填好vitual字段的page结构    建立永久地址映射,不是简单的返回virtual字段的pageioremap:    驱动程序 ...

  9. Nah Lock: 一个无锁的内存分配器

    概述 我实现了两个完全无锁的内存分配器:_nalloc 和 nalloc.  我用benchmark工具对它们进行了一组综合性测试,并比较了它们的指标值. 与libc(glibc malloc)相比, ...

随机推荐

  1. Do in SDN

    Do in SDN 书籍 <深度解析SDN 利益.战略.战术.实践> 张卫锋 <重构网络-SDN构架与实现>杨泽卫.李呈 <软件定义网络核心原理与应用实践> 黄韬. ...

  2. 如何判断可见字符 Unicode

    一个Unicode字符串,如何判断其中都是可见字符? //根据国标 GB2312 的中文汉字及符号 区位码的范围判断 Function CheckIsGB2312(Char : WideChar) : ...

  3. centos 7 安装搜狗输入法

    1.安装alien依赖软件sudo yum install alien -y 2.安装依赖软件sudo yum install qtwebkit -ysudo yum install fcitx -y ...

  4. QT试验(1)

    今天,用Qt Creator写第一个简单的小程序“HelloQt”,运行时报错:error: cannot open C:\Users\敏\AppData\Local\Temp\main.obj.51 ...

  5. SpringBoot 5.SpringBoot小知识讲解

    1.修改 server 端口: 在 application.properties 中添加 server.port=9090,我们的端口号就会变成9090了. 2.自定义配置Web: 2.1 创建 Cu ...

  6. c/c++ 函数说明以及技巧总结

    1. memset函数: void *memset(void *s, int ch, size_t n); 函数解释:将s中当前位置后面的n个字节 (typedef unsigned int size ...

  7. word 里面没输入法

    文件,选项,高级,输入法控制处于活动状态   ,有勾选就去掉,无勾选就勾上,确定后重开word即可

  8. SQLServer过期的解决方案

    看图吧,不喜欢说话,图里面我都打备注了 0SQLService异常 1找到安装中心 2升级版本 3监测ing 4输入升级key 5同意并下一步 6下一步 7下一步 8下一步 9收工 10可以打开了

  9. Python Socket函数及说明

  10. BFC的个人理解

    BFC是Block Formatting Context (块级格式化上下文)的缩写,是一个独立的渲染区域,这个东西的存在是为了隔绝一些内部子元素对外部元素的影响. 例如: 我们用overflow:h ...