转自:http://blog.csdn.net/kickxxx/article/details/54710243

版权声明:本文为博主原创文章,未经博主允许不得转载。

目录(?)[-]

  1. 背景
  2. Data structure
  3. memblock initialization
  4. memblock API
  5. memory和reserved region
    1. memblock_reserved
    2. memblock_alloc
    3. memblock_free

背景

在Linux内核开发过程中, 多少都会存在一个patch, 引入了远超预期的麻烦. 内核2.6.34开发过程中, 这个奖项非CONFIG_NO_BOOTMEM莫属

bootmem本身是个简单的,低级的内存分配器. 在引导程序的初期用来分配内存. 有人可能会想, 没有必要再增加一个内存分配器, 但是由于内存管理代码在被调用前需要很多内核功能都准备好, 要想在启动初期使用内存管理代码会大大增加内存管理的复杂性. 在x86架构上, 会首先使用early_res机制接替BIOS e820的工作, 然后再交给架构独立的bootmem分配器, 最后才是全功能的buddy allocator

YingHai LU认为可以把bootmem从这个过程中去掉, 简化这个过程. 结果就是, 增加了一堆patch来扩展early_res机制, 把本该交给bootmem做的事情都做了, 然后直接到buddy分配器. 这些修改被合入了2.6.34, 老的基于bootmem的代码仍然保留. CONFIG_NO_BOOTMEM用来控制使用哪个分配器, 缺省情况下并不使用bootmem

一切本该非常美好, 但是随着kernel rc版本的发布, 新分配器测试陆续爆出很多问题, Linus发了一封邮件要求revert掉整个patch. 虽然简化代码这个注意看起来不错, 但是rc3仍然导致系统死机, 以及大量使用ifdef, 以及缺省打开CONFIG_NO_BOOTMEM, 导致了社区的怨气.

正常情况下, 新功能缺省情况下是不使能的. 新kernel应该尽最大可能和之前的kernel保持一致. 而CONFIG_NO_BOOTMEM缺省打开导致了很大的改变和问题.

Yinghai在2.6.35基础上又提交了一组patch, 使用logical memory block分配器替代early_res代码, 这组patch看起来比删除bootmem引入了更大的风险

--https://lwn.NET/Articles/382559/

在Yinghai删除bootmem patch的review过程中, 一些reviewers质疑为什么x86不使用logical memory block(LMB)分配器替换early-res的代码. 当X86使用类似brk()形式的early-res时 Microblaze, PowerPC, SuperH和SPARC架构已经使用LMB进行系统启动初期的内存分配, 所以LMB可以看做是一个generic的解决方案. 使用通用代码的好处是显而易见的: 更多的人review代码, 总体维护代价更带. 所以使用LMB明显更合理.

因此Yinghai在2.6.35上又提交了一组patch来简化启动分配器代码

Data structure

[cpp] view plaincopy
  1. struct memblock {
  2. bool bottom_up;  /* is bottom up direction? */
  3. phys_addr_t current_limit;
  4. struct memblock_type memory;
  5. struct memblock_type reserved;
  6. };

bootom_up 设置为true时, 允许内存分配使用bottom-up模式

current_limit memblock分配内存时的上限

memory 描述了当前内存区包含的内存区数目, 总大小, 以及每个内存region

reserved 描述了当前内存块已经分配的内存区数目, 总大小,以及每个内存region. 在reserved中描述的地址范围, 表示不可以再被memblock分配.

memory用来描述memblock全部内存region(不区分分配和未分配), reserved用来描述memblock中已经分配的内存region

[cpp] view plaincopy
  1. struct memblock_type {
  2. unsigned long cnt;  /* number of regions */
  3. unsigned long max;  /* size of the allocated array */
  4. phys_addr_t total_size; /* size of all regions */
  5. struct memblock_region *regions;
  6. };

cnt         regions数目

max       最大regions数目

total_size    regions总尺寸

regions        regions array

[cpp] view plaincopy
  1. struct memblock_region {
  2. phys_addr_t base;
  3. phys_addr_t size;
  4. unsigned long flags;
  5. #ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
  6. int nid;
  7. #endif
  8. };

base      region 基地址

size        region size

flags      region

memblock initialization

[cpp] view plaincopy
  1. static struct memblock_region memblock_memory_init_regions[INIT_MEMBLOCK_REGIONS] __initdata_memblock;
  2. static struct memblock_region memblock_reserved_init_regions[INIT_MEMBLOCK_REGIONS] __initdata_memblock;
  3. struct memblock memblock __initdata_memblock = {
  4. .memory.regions     = memblock_memory_init_regions,
  5. .memory.cnt     = 1,    /* empty dummy entry */
  6. .memory.max     = INIT_MEMBLOCK_REGIONS,
  7. .reserved.regions   = memblock_reserved_init_regions,
  8. .reserved.cnt       = 1,    /* empty dummy entry */
  9. .reserved.max       = INIT_MEMBLOCK_REGIONS,
  10. .bottom_up      = false,
  11. .current_limit      = MEMBLOCK_ALLOC_ANYWHERE,
  12. };

.memory.regions 和reserved.regions固定数组, 最多支持128个regions

memory.mx和reserved.max的最大值也为128

current_limit 也被定义为最大可能物理地址

memblock API

在include/linux/memblock.h中定义了memblock的API

[cpp] view plaincopy
  1. int __init_memblock memblock_reserve(phys_addr_t base, phys_addr_t size)
  2. {
  3. return memblock_reserve_region(base, size, MAX_NUMNODES, 0);
  4. }

memblock_reserve API主要是为系统启动阶段为kernel(text, data and initrd), swapper_pg_dir, reserved-memory, memreserve等预留内存.

[cpp] view plaincopy
  1. int __init_memblock memblock_remove(phys_addr_t base, phys_addr_t size)
  2. {
  3. return __memblock_remove(&memblock.memory, base, size);
  4. }

系统中有两处会调用memblock_remove:

1. early_init_dt_reserve_memory_arch中, 如果不希望这段内存被映射, 那么就调用memblock_remove, 把这段内存从memblock.memory中移除

2. arm_memblock_steal中, 调用memblock_remove从memblock.memory中偷一段内存空间, memblock.memory的regions中将不再包含这段内存空间.

[cpp] view plaincopy
  1. int __init_memblock memblock_add(phys_addr_t base, phys_addr_t size)
  2. {
  3. return memblock_add_region(&memblock.memory, base, size,
  4. MAX_NUMNODES, 0);
  5. }

在memblock的memory type中增加一个region

base   是新增region的基地址

size    是新增region的尺寸

对于arm, 仅一处会调用memblock_add: arm_memblock_init 中根据meminfo向memblock.memory中增加region

memory和reserved region

memblock有两个memblock_type成员: memory和reserved

memblock.memory 描述memblock所有内存区(已分配的+未分配的)

memblock.reserved 描述已经分配的内存区, 所有分配和释放操作都是通过修改reserved来实现的. 分配操作在memblock.reserved上增加region, 释放操作则memblock.reserved上释放region.

memblock_reserved

[cpp] view plaincopy
  1. int __init_memblock memblock_reserve(phys_addr_t base, phys_addr_t size)
  2. {
  3. return memblock_reserve_region(base, size, MAX_NUMNODES, 0);
  4. }
  5. static int __init_memblock memblock_reserve_region(phys_addr_t base,
  6. phys_addr_t size,
  7. int nid,
  8. unsigned long flags)
  9. {
  10. struct memblock_type *_rgn = &memblock.reserved;
  11. return memblock_add_region(_rgn, base, size, nid, flags);
  12. }

memblock_reserve -> memblock_reserve_region -> memblock_add_region

调用memblock_add_region时第一个参数为 memblock.reserved, 也就是说在memblock.reserved增加一个region

memblock_add_region流程如下:

1. 如果type->regions[0].size==0, 表示regions数组为空, 添加region[0], 设置region[0]相关成员,即可返回. 此时该memblock_type有一个region了

2. 计算要插入的region数目, [base, base + size]可能会跨越多个已存在的region, 因此数目可能不为1.

3. 由于要插入新region, 所以需要先扩展regions array.

4. 插入这些regions

5. 执行region merge操作

我们可以看到memblock_reserve()操作压根就没考虑memblock.memory, 因为memblock_reserve()只是告诉memblock, 这部分内存被保留了, 不要再参与memblock分配操作

memblock_alloc

[cpp] view plaincopy
  1. phys_addr_t __init memblock_alloc(phys_addr_t size, phys_addr_t align)
  2. {
  3. return memblock_alloc_base(size, align, MEMBLOCK_ALLOC_ACCESSIBLE);
  4. }
  5. phys_addr_t __init memblock_alloc_try_nid(phys_addr_t size, phys_addr_t align, int nid)
  6. {
  7. phys_addr_t res = memblock_alloc_nid(size, align, nid);
  8. if (res)
  9. return res;
  10. return memblock_alloc_base(size, align, MEMBLOCK_ALLOC_ACCESSIBLE);
  11. }

memblock_alloc->memblock_alloc_base->memblock_alloc_base_nid

[cpp] view plaincopy
  1. static phys_addr_t __init memblock_alloc_base_nid(phys_addr_t size,
  2. phys_addr_t align, phys_addr_t max_addr,
  3. int nid)
  4. {
  5. phys_addr_t found;
  6. if (!align)
  7. align = SMP_CACHE_BYTES;
  8. found = memblock_find_in_range_node(size, align, 0, max_addr, nid);
  9. if (found && !memblock_reserve(found, size))
  10. return found;
  11. return 0;
  12. }

1. memblock_find_in_range_node查找符合条件的物理地址, 查找过程会涉及到查看memblock.reserve

2. 如果找到了这个物理地址, 调用memblock_reserve进行真正的分配(就是在memblock.reserve中添加region)

memblock_free

释放参数指定的内存区间

[cpp] view plaincopy
  1. int __init_memblock memblock_free(phys_addr_t base, phys_addr_t size)
  2. {
  3. memblock_dbg("   memblock_free: [%#016llx-%#016llx] %pF\n",
  4. (unsigned long long)base,
  5. (unsigned long long)base + size - 1,
  6. (void *)_RET_IP_);
  7. return __memblock_remove(&memblock.reserved, base, size);
  8. }

逻辑很简单, 从memblock.reserved中删除[base, base+size]指定范围的region. 这样下次调用memblock_alloc时再检查reserved type时, 这段区域可以再次使用了

【作者】张昺华
【出处】http://www.cnblogs.com/sky-heaven/
【博客园】 http://www.cnblogs.com/sky-heaven/
【新浪博客】 http://blog.sina.com.cn/u/2049150530
【知乎】 http://www.zhihu.com/people/zhang-bing-hua
【我的作品---旋转倒立摆】 http://v.youku.com/v_show/id_XODM5NDAzNjQw.html?spm=a2hzp.8253869.0.0&from=y1.7-2
【我的作品---自平衡自动循迹车】 http://v.youku.com/v_show/id_XODM5MzYyNTIw.html?spm=a2hzp.8253869.0.0&from=y1.7-2
【新浪微博】 张昺华--sky
【twitter】 @sky2030_
【facebook】 张昺华 zhangbinghua
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.

内存分配器memblock【转】相关推荐

  1. linux内存初始化初期内存分配器——memblock

    2019独角兽企业重金招聘Python工程师标准>>> 1.1.1 memblock 系统初始化的时候buddy系统,slab分配器等并没有被初始化好,当需要执行一些内存管理.内存分 ...

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

    原标题:内核早期内存分配器:memblock 本文转载自Linux爱好者 本文来自 程雪涛的自荐投稿 Linux内核使用伙伴系统管理内存,那么在伙伴系统工作前,如何管理内存?答案是memblock. ...

  3. Linux内核内存管理(1):内存块 - memblock

    Linux内核内存管理 内存块 - memblock rtoax 2021年3月 在英文原文基础上,针对中文译文增加5.10.13内核源码相关内容. 1. 简介 内存管理是操作系统内核中最复杂的部分之 ...

  4. linux内存管理(五)-引导内存分配器

    linux内存三大分配器:引导内存分配器,伙伴分配器,slab分配器 一.引导内存分配器 1.引导内存分配器的作用 因为内核里面有很多内存结构体,不可能在静态编译阶段就静态初始化所有的这些内存结构体. ...

  5. 深入浅出内存管理-memblock

    memblock 介绍 memblock 内存管理机制主要用于Linux Kernel 启动阶段(kernel启动 -> kernel 通用内存管理初始化完成.) 或者可以认为free_init ...

  6. Linux内核机制总结内存管理之连续内存分配器(二十七)

    文章目录 1 连续内存分配器 1.1 使用方法 1.2 技术原理 重要:本系列文章内容摘自<Linux内核深度解析>基于ARM64架构的Linux4.x内核一书,作者余华兵.系列文章主要用 ...

  7. 内存分配器设计的演进

    文章目录 栈内存空间是否够用 系统调用申请内存 最简单的内存分配器实现 -- bump allocator 可扩容的 Bump alloactor 通过free-list 管理的 allocator ...

  8. mysql 自动管理内存_MySQL内存管理,内存分配器和操作系统

    导读 作者:Sveta Smirnova 翻译:郑志江 校对:徐晨亮 原文 :MySQL Memory Management, Memory Allocators and Operating Syst ...

  9. slab 内存分配器介绍(一)

    原文引用地址:https://www.ibm.com/developerworks/cn/linux/l-linux-slab-allocator/ 动态内存管理 内存管理的目标是提供一种方法,为实现 ...

最新文章

  1. IPv6 与 IPv4现状
  2. WebStack-Hugo | 一个静态响应式网址导航主题
  3. Kaggle常用函数总结 原创 2017年07月03日 21:47:34 标签: kaggle 493 kaggle比赛也参加了好几次,在这里就把自己在做比赛中用到的函数汇总到这,方便自己以后查阅
  4. 高级软件工程第九次作业:东理三剑客团队作业-随笔2
  5. 在Linux上安装nginx时遇到的问题,真的好坑啊!!!!
  6. 【HDU - 6441】Find Integer (费马大定理 + 奇偶数列法构造勾股定理)
  7. 静态页面实现include
  8. ======第二章进程管理======
  9. Linux Apache服务详解——Apache虚拟目录与禁止显示目录列表实战
  10. 实对称矩阵的特征值求法_“绝境之下”,如何求解矩阵的特征值?
  11. Charles进行弱网测试
  12. 医学超声成像—合成孔径法(Synthetic Aperture Methods)[1]
  13. 关于打开ARX项目的一些错误
  14. 接口测试项目(非常值得练手)
  15. Projector的用法
  16. Ubuntu Desktop LTS - 快速显示桌面
  17. VB制作的“小小莫扎特五线谱助记软件”
  18. 壁纸网站研究:强大到没朋友的壁纸网站整理(动漫/二次元/宅男/风景/真人)
  19. [置顶] wifi漫游测试过程
  20. UBC 计算机专业 要求 英语 A,赴加拿大留学语言成绩的要求

热门文章

  1. python---简单数据库
  2. 别得意,你只是假装收藏了而已
  3. python 验证码识别示例(二) 复杂验证码识别
  4. Wedge 100-32X 100GbE Data Center Switch
  5. 01 使用AFN3 0上传图片时间慢的问题
  6. 黑客内参告诉你一个:设计师用div+css 必须知道的网页布局类型
  7. Linux--文件管理以及权限的修改
  8. 数组、字符串对象、Math对象
  9. 《Java 开发从入门到精通》—— 2.2 编写第一段Java程序
  10. rsync 同步数据