上文我们讲到快速分配和慢速分配,接下来会详细讲解这两种分配情况,我们先来看下快速分配:

static struct page *get_page_from_freelist(gfp_t gfp_mask, unsigned int order, int alloc_flags,const struct alloc_context *ac){for_next_zone_zonelist_nodemask(zone, z, ac->zonelist, ac->high_zoneidx, ac->nodemask){if (!zone_watermark_fast(zone, order, mark, ac_classzone_idx(ac), alloc_flags)){ret = node_reclaim(zone->zone_pgdat, gfp_mask, order);switch (ret) {case NODE_RECLAIM_NOSCAN:continue;case NODE_RECLAIM_FULL:continue;default:if (zone_watermark_ok(zone, order, mark, ac_classzone_idx(ac), alloc_flags))goto try_this_zone;

continue;}}

try_this_zone: //本zone正常水位page = rmqueue(ac->preferred_zoneref->zone, zone, order, gfp_mask, alloc_flags, ac->migratetype);}

return ;}

首先遍历当前zone,按照HIGHMEM->NORMAL的方向进行遍历,判断当前zone是否能够进行内存分配的条件是首先判断free memory是否满足low water mark水位值,如果不满足则进行一次快速的内存回收操作,然后再次检测是否满足low water mark,如果还是不能满足,相同步骤遍历下一个zone,满足的话进入正常的分配情况,即rmqueue函数,这也是伙伴系统的核心。

Buddy 分配算法

在看函数前,我们先看下算法,因为我一直认为有了“道”的理解才好进一步理解“术”。

假设这是一段连续的页框,阴影部分表示已经被使用的页框,现在需要申请一个连续的5个页框。这个时候,在这段内存上不能找到连续的5个空闲的页框,就会去另一段内存上去寻找5个连续的页框,这样子,久而久之就形成了页框的浪费。为了避免出现这种情况,Linux内核中引入了伙伴系统算法(Buddy system)。把所有的空闲页框分组为11个块链表,每个块链表分别包含大小为1,2,4,8,16,32,64,128,256,512和1024个连续页框的页框块。最大可以申请1024个连续页框,对应4MB大小的连续内存。每个页框块的第一个页框的物理地址是该块大小的整数倍,如图:

假设要申请一个256个页框的块,先从256个页框的链表中查找空闲块,如果没有,就去512个页框的链表中找,找到了则将页框块分为2个256个页框的块,一个分配给应用,另外一个移到256个页框的链表中。如果512个页框的链表中仍没有空闲块,继续向1024个页框的链表查找,如果仍然没有,则返回错误。页框块在释放时,会主动将两个连续的页框块合并为一个较大的页框块。

从上面可以知道Buddy算法一直在对页框做拆开合并拆开合并的动作。Buddy算法牛逼就牛逼在运用了世界上任何正整数都可以由2^n的和组成。这也是Buddy算法管理空闲页表的本质。空闲内存的信息我们可以通过以下命令获取:

也可以通过echo m > /proc/sysrq-trigger来观察buddy状态,与/proc/buddyinfo的信息是一致的:

Buddy 分配函数

static inlinestruct page *rmqueue(struct zone *preferred_zone,struct zone *zone, unsigned int order,gfp_t gfp_flags, unsigned int alloc_flags,int migratetype){if (likely(order == 0)) { //如果order=0则从pcp中分配page = rmqueue_pcplist(preferred_zone, zone, order, gfp_flags, migratetype);}do {page = ;if (alloc_flags & ALLOC_HARDER) {//如果分配标志中设置了ALLOC_HARDER,则从free_list[MIGRATE_HIGHATOMIC]的链表中进行页面分配page = __rmqueue_smallest(zone, order, MIGRATE_HIGHATOMIC);}if (!page) //前两个条件都不满足,则在正常的free_list[MIGRATE_*]中进行分配page = __rmqueue(zone, order, migratetype);} while (page && check_new_pages(page, order));......}

可以看出伙伴系统在页面进行分配的时候,有以下四个步骤:

  1. 如果申请的是order = 0的页面,直接选择从pcp中进行分配,并直接退出;

  2. order > 0时,如果分配标志中设置了ALLOC_HARDER,则从free_list[MIGRATE_HIGHATOMIC]的链表中进行页面分配,分配成功则返回;

  3. 前两个条件都不满足,则在正常的free_list[MIGRATE_*]中进行分配,分配成功则直接则返回;

  4. 如果3中分配失败了,则查找后备类型fallbacks[MIGRATE_TYPES][4],并将查找到的页面移动到所需的MIGRATE类型中,移动成功后,重新尝试分配;

「__rmqueue_smallest:」

static inlinestruct page *__rmqueue_smallest(struct zone *zone, unsigned int order,int migratetype){unsigned int current_order;struct free_area *area;struct page *page;

/* Find a page of the appropriate size in the preferred list */for (current_order = order; current_order < MAX_ORDER; ++current_order) {area = &(zone->free_area[current_order]); //得到指定order的areapage = list_first_entry_or_(&area->free_list[migratetype],struct page, lru);if (!page) //如果area指定类型的伙伴系统链表为空continue; //查找下一个orderlist_del(&page->lru); //从伙伴系统中删除rmv_page_order(page); //移除page中order的变量area->nr_free--; //空闲块数减一expand(zone, page, order, current_order, area, migratetype);//查找到页表之后,从对应的链表中拆分合并set_pcppage_migratetype(page, migratetype);return page;}

return ;}

即:

  1. 从申请的order大小开始查找目标MIGRATE类型链表中页表,如果没有找到,则从更大的order中查找,直到MAX_ORDER;

  2. 查找到页表之后,从对应的链表中删除掉,并调用expand函数进行处理;

「__rmqueue:」

static struct page *__rmqueue(struct zone *zone, unsigned int order,int migratetype){page = __rmqueue_smallest(zone, order, migratetype);//从指定order开始从小到达遍历,优先从指定的迁移类型链表中分配页面if (unlikely(!page)) {if (migratetype == MIGRATE_MOVABLE)page = __rmqueue_cma_fallback(zone, order);

if (!page && __rmqueue_fallback(zone, order, migratetype))//从备用fallbacks中找到一个迁移类型页面块,将其移动到目标类型中,并重新进行分配。goto retry;}

return page;}

可以看出在正常的free_list[MIGRATE_*]分配失败后,会查找后备类型fallbacks[MIGRATE_TYPES][4],并将查找到的页面移动到所需的MIGRATE类型中,移动成功后,重新尝试分配;

static int fallbacks[MIGRATE_TYPES][4] = {[MIGRATE_UNMOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_MOVABLE, MIGRATE_TYPES },[MIGRATE_RECLAIMABLE] = { MIGRATE_UNMOVABLE, MIGRATE_MOVABLE, MIGRATE_TYPES },[MIGRATE_MOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_TYPES },#ifdef CONFIG_CMA[MIGRATE_CMA] = { MIGRATE_TYPES }, /* Never used */#endif#ifdef CONFIG_MEMORY_ISOLATION[MIGRATE_ISOLATE] = { MIGRATE_TYPES }, /* Never used */#endif};

__rmqueue_fallback完成的主要工作就是从后备fallbacks中找到一个迁移类型页面块,将其移动到目标类型中,并重新进行分配。

以上就是伙伴系统的分配过程。

伙伴算法的核心思想是回收时进行相邻块的合并_Linux内存管理之伙伴算法相关推荐

  1. 【算法特训总结】计算机经典算法的核心思想及独特角度的解读

    计算机经典算法的核心思想及独特角度的解读 在1月1日新年之日开始的"算法特训"(一月一日~二月十日)终于结束了,对于这本<<算法竞赛经典>>,除了第十章(在 ...

  2. 回溯算法:从电影蝴蝶效应中学习回溯算法的核心思想

    回溯算法:从电影<蝴蝶效应>中学习回溯算法的核心思想 数独.八皇后.0-1背包.图的着色.旅行商问题.全排列问题都能用到 理解"回溯算法" 回溯的思想,类似枚举搜索,枚 ...

  3. Linux系统内存管理之伙伴系统分析 - 旭东的博客 - 博客园

    Linux系统内存管理之伙伴系统分析 - 旭东的博客 - 博客园 Linux系统内存管理之伙伴系统分析 今天去面试,一位面试官提到了内存管理的伙伴系统,当时就懵了,因为根本就没有听说过.晚上回来在实验 ...

  4. kmeans算法的核心思想和实现逻辑流程

    1 概述 K-means算法是集简单和经典于一身的基于距离的聚类算法 采用距离作为相似性的评价指标,即认为两个对象的距离越近,其相似度就越大. 该算法认为类簇是由距离靠近的对象组成的,因此把得到紧凑且 ...

  5. 极客时间——数据结构与算法(39) 回溯算法:从电影《蝴蝶效应》中学习回溯算法的核心思想

    转载地址:https://time.geekbang.org/column/article/74287 我们在第 31 节提到,深度优先搜索算法利用的是回溯算法思想.这个算法思想非常简单,但是应用却非 ...

  6. 嵌入式Linux驱动笔记(二十九)------内存管理之伙伴算法(Buddy)分析

    你好!这里是风筝的博客, 欢迎和我一起交流. 我们知道,在一个通用操作系统里,频繁申请内存释放内存都会出现一个非常著名的内存管理问题:内存碎片. 学过操作系统的都知道,有很多行之有效的方法(比如:记录 ...

  7. java 最少使用(lru)置换算法_[内附完整源码和文档] 基于C#的可视化虚拟存储器管理(LUR算法)...

    一.目的要求 理解虚拟存储器概念,并掌握分页式存储管理地址转换和缺页中断的处理过程.用高级语言模拟请求分页式虚拟存储器的工作过程和页面置换算法LRU. 二.准备知识 2.1 分页式存储管理原理 在存储 ...

  8. 从电影《蝴蝶效应》中学习回溯算法的核心思想

    点击上方"视学算法",选择加"星标"或"置顶" 重磅干货,第一时间送达 关注我们丨文末赠书 深度优先搜索算法利用的是回溯算法思想.这个算法思 ...

  9. 回溯算法:从电影《蝴蝶效应》中学习回溯算法的核心思想

    ------ 本文是学习算法的笔记,<数据结构与算法之美>,极客时间的课程 ------ 我们在深度和广度优先算法提到,深度优先搜索算法利用的是回溯算法思想.这个算法思想非常简单,但是应用 ...

  10. 39.回溯算法:从电影《蝴蝶效应》中学习回溯算法的核心思想

    文章目录 1. 如何理解"回溯算法"? 2. 两个回溯算法的经典应用 2.1 0-1背包 2.2 正则表达式 回溯算法解决,比如数独.八皇后.0-1背包.图的着色.旅行商问题.全排 ...

最新文章

  1. JS+库+框架+工具
  2. python卸载pip重新安装_pip无法正常使用卸载并重新安装
  3. [转]查询oracle数据库的数据库名、实例名、ORACLE_SID
  4. 卡顿严重_王者峡谷:S20出现bug?卡顿十分严重
  5. U3D的结构体堆分配栈分配
  6. Linux16.04LTS 安装Intel RealSense D435驱动
  7. Java中的数据类型及相互转换方法
  8. JENKINS+maven+ssh+shell 完成自动化部署工具的开发
  9. 高级JAVA - 利用函数式接口实现通用的取并集/交集/差集
  10. 7、编译安装LAMP之apache与PHP整合
  11. sql加上唯一索引后批量插入_MySQL批量插入遇上唯一索引避免方法
  12. android 支持swf格式,安卓手机如何播放swf文件
  13. 分享5个苹果系统超实用的黑科技APP,个个都是精品
  14. Exadata使用EXAchk进行健康检查
  15. javaScript实现a页面触发b页面事件-小小笔记
  16. 二维码 编码原理简介
  17. C++继承以及菱形继承
  18. Java从零开始实现导出excel(一)
  19. 跳转到指定的邮箱登录页面
  20. 计算机还硬盘后怎么兼容,电脑升级ssd固态硬盘后为什么还是卡?老电脑升级固态硬盘注意事项详解...

热门文章

  1. dubbo源码系列之filter的前生
  2. python 环境问题
  3. 《用户至上:用户研究方法与实践》道德与法律问题
  4. 加密--HashPasswordForStoringInConfigFile过时问题
  5. 快讯:百度正式宣布CTO李一男离职
  6. AJAX技术开发Back按钮问题的应用程序
  7. 利用IP标准访问列表进行网络流量的控制
  8. 我的博客也是男的(还好)
  9. WM 仓库管理T-CODE
  10. 域名虚拟主机管理系统linux,8 款顶级的虚拟主机管理系统