内核早期内存分配器: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. Day Nine

    站立式会议 站立式会议内容总结 331 今天:学习plupload 遇到问题:无 明天:学习中文分词 442 今天:解决gradle以及项目计划页面的bug 遇到的问题:调用工具类以及配置gradle ...

  2. Java运算符、switch、数组、排序

    1.Java的运算符,分为四类:算数运算符.关系运算符.逻辑运算符.位运算符 运算符例子:22.25(十进制转化为二进制,8421码)0010 0010 (22)0010 0101 (25) 位运算符 ...

  3. HDU 2088 Box of Bricks

    http://acm.hdu.edu.cn/showproblem.php?pid=2088 Problem Description Little Bob likes playing with his ...

  4. Spring源码学习:DefaultAopProxyFactory

    /* * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Vers ...

  5. ORM的详解

    有很多小伙伴都不太理解ORM是什么,其实不用想象的那么复杂.我们先根据3W1H去理解. who:首先ORM可以立即为(Object/Relation Mapping): 对象/关系映射 what:其次 ...

  6. 使用fiddler的过滤条件

    使用fiddler抓包的时候经常一下子显示很多的记录,看的眼花缭乱,需要这时候需要使用过滤条件来帮助你,一般常用的有三种过滤条件: 1.域名过滤,只显示特定域名的记录: *.baidu.com表示所有 ...

  7. Emacs 安装配置使用教程

    Emacs 安装配置使用教程 来源 https://www.jianshu.com/u/a27b97f900f7 序|Preface 先来一篇有趣的简介:Emacs和Vim:神的编辑器和编辑器之神 - ...

  8. Different between Telnet/SSH/FTP

    http://www.differencebetween.net/category/technology/protocols-formats/ Telnet vs SSH Secure Shell, ...

  9. 【BZOJ1965】[AHOI2005]洗牌(数论)

    [BZOJ1965][AHOI2005]洗牌(数论) 题面 BZOJ 洛谷 题解 考虑反过来做这个洗牌的操作,假定当前牌是第\(l\)张. 因为之前洗的时候考虑了前一半和后一半,所以根据\(l\)的奇 ...

  10. Libre 6010「网络流 24 题」数字梯形 (网络流,最大费用最大流)

    Libre 6010「网络流 24 题」数字梯形 (网络流,最大费用最大流) Description 给定一个由n 行数字组成的数字梯形如下图所示.梯形的第一行有m 个数字.从梯形的顶部的m 个数字开 ...