Kernel中负责分配一个连续内存页块的子系统一般被称为zoned page frame allocator。前面讲了函数 buffered_rmqueue() 是如何从指定zone的buddy system中分配一个连续内存页块的。这个函数貌似完成了内存页块分配相关的所有工作,然而实际上,这个函数只是zone allocator的冰山一角。

记得我刚上大学那会,拥有一个MP3还是一件能够令男生羡慕、令女生着迷的事情。于是我咬咬牙,花了近一个月的生活费买了一个漂亮的MP3,从此过上了非凡的生活。。。

可惜好景不长,没过多久就坏掉了。我拿去维修,只见一个年轻小伙拿着电烙铁点了几下,居然就修好了。给我要很贵的维修费,我就很不情愿,说:“就点了几下,要这么贵啊。” 然后那小伙回答:“点这几下不贵,但是知道往哪里点,这就贵了。” 我深以为然,重重地点了点头,心甘情愿地把钱交了。

同样的道理,buffered_rmqueue()从指定zone的buddy system中分配内存页块,这个不复杂;但是能够确定从哪个zone中分配,以及确定某个zone什么情况下可以分配,这个就复杂多了。而这,正是zone allocator的核心所在。

在正式介绍zone allocator之前,先介绍一些周边事务。

1. zonelist

这个东东在之前的博文里经过,直接copy过来:

node_zonelists: 当我们要在某一个指定的zone中分配内存,而该zone中又没有足够多的空闲内存时,怎么办?正所谓狡兔三窟,我们得给自己留点后路,node_zonelists正是这种情况下的后路。它指定了所有的备选的zones。当然这些备选zones也是有优先顺序的,毕竟只有小三也不能满足需求时,才会再去找小四。

2. zone_watermark_ok()

一个zone在用来分配内存页块之前,得先有人把把关,姓甚名谁,家里几口人,人均几亩地,地里几头牛,总得检查检查。这个函数就是这样的把关函数。

这个函数会用到一些flags。

1112 #define ALLOC_NO_WATERMARKS 0x01 /* don't check watermarks at all */
1113 #define ALLOC_WMARK_MIN     0x02 /* use pages_min watermark */
1114 #define ALLOC_WMARK_LOW     0x04 /* use pages_low watermark */
1115 #define ALLOC_WMARK_HIGH    0x08 /* use pages_high watermark */
1116 #define ALLOC_HARDER        0x10 /* try to alloc harder */
1117 #define ALLOC_HIGH          0x20 /* __GFP_HIGH set */
1118 #define ALLOC_CPUSET        0x40 /* check for correct cpuset */

这些flags用来指定把关时使用哪个watermark值。

1219 int zone_watermark_ok(struct zone *z, int order, unsigned long mark,
1220               int classzone_idx, int alloc_flags)
1221 {
1222     /* free_pages my go negative - that's OK */
1223     long min = mark;
1224     long free_pages = zone_page_state(z, NR_FREE_PAGES) - (1 << order) + 1;
1225     int o;
1226
1227     if (alloc_flags & ALLOC_HIGH)
1228         min -= min / 2;
1229     if (alloc_flags & ALLOC_HARDER)
1230         min -= min / 4;
1231
1232     if (free_pages <= min + z->lowmem_reserve[classzone_idx])
1233         return 0;
1234     for (o = 0; o < order; o++) {
1235         /* At the next order, this order's pages become unavailable */
1236         free_pages -= z->free_area[o].nr_free << o;
1237
1238         /* Require fewer higher order pages to be free */
1239         min >>= 1;
1240
1241         if (free_pages <= min)
1242             return 0;
1243     }
1244     return 1;
1245 }

该函数首先确定该zone中的空闲页面个数,以及用于threshold的变量min的值。

从 1227 ~ 1230 可以看到,如果设置了ALLOC_HIGH 或 ALLOC_HARDER,min的值会进一步减少,这也就意味着把关条件放松,分配会更加aggressive。

然后开始把关测试,如果满足以下两个条件,则测试通过:

1)除去要分配的页面个数外,该zone中至少要包含这么多空闲页面:

  • min 个空闲页面

  • lowmem_reserve 中指定要预留的页面个数

这里lowmem_reserve是Kernel的一个非常聪明的机制,用来防止来自higher zone的分配请求过多的消耗lower zone中的内存。

这个有点不好理解,举个例子,假设zone->lowmem_reserve[ZONE_HIGHMEM] 的值为R,这个值表示的是:对于来自ZONE_HIGHMEM的内存分配请求,若是想从本zone中分配页面,那么你至少要给我保留R个页面。

2)除去要分配的页面个数外,从order k 到 order 10的空闲页面总数,至少得是 min/(2^k)。其中k 取值范围是从1 到 参数指定的order值。

这个更不好理解了,还是举个例子。如果 k=2 的话,则从order 2 到order 10的空闲页面总数,至少得是 min/4。

如果满足以上两点,恭喜你,通过了把关测试。

3. get_page_from_freelist()

有了前面的准备,这个函数的工作就很轻松了。

它会按序遍历zonelist中所有的zone,对于每个zone,先用函数zone_watermark_ok()来把把关,如果把关通过,再用函数buffered_rmqueue()来从buddy system中申请内存页块。

1371 static struct page *
1372 get_page_from_freelist(gfp_t gfp_mask, unsigned int order,
1373         struct zonelist *zonelist, int alloc_flags)
1374 {
1375     struct zone **z;
1376     struct page *page = NULL;
1377     int classzone_idx = zone_idx(zonelist->zones[0]);
1378     struct zone *zone;...
1383
1384 zonelist_scan:
1385     /*
1386      * Scan zonelist, looking for a zone with enough free.
1387      * See also cpuset_zone_allowed() comment in kernel/cpuset.c.
1388      */
1389     z = zonelist->zones;
1390
1391     do {...1407         zone = *z;
1408         if ((alloc_flags & ALLOC_CPUSET) &&
1409             !cpuset_zone_allowed_softwall(zone, gfp_mask))
1410                 goto try_next_zone;
1411
1412         if (!(alloc_flags & ALLOC_NO_WATERMARKS)) {
1413             unsigned long mark;
1414             if (alloc_flags & ALLOC_WMARK_MIN)
1415                 mark = zone->pages_min;
1416             else if (alloc_flags & ALLOC_WMARK_LOW)
1417                 mark = zone->pages_low;
1418             else
1419                 mark = zone->pages_high;
1420             if (!zone_watermark_ok(zone, order, mark,
1421                     classzone_idx, alloc_flags)) {
1422                 if (!zone_reclaim_mode ||
1423                     !zone_reclaim(zone, gfp_mask, order))
1424                     goto this_zone_full;
1425             }
1426         }

cpuset_zone_allowed_softwall()用来做cpuset相关检查,暂且忽略。

该函数首先解析ALLOC_* flags,从而确定使用哪个watermark。watermark的高低,直接影响到了把关时是宽松还是严格。

注意,如果设置了ALLOC_NO_WATERMARKS,则跳过把关函数,无条件放行。

然后利用函数zone_watermark_ok()来检查该zone中是否有足够多的空闲页面。

如果把关通过,接下来就是与buddy system打交道了,这个工作由之前讲过的buffered_rmqueue()来完成。

1428         page = buffered_rmqueue(zonelist, zone, order, gfp_mask);
1429         if (page)
1430             break;
1431 this_zone_full:
1432        ...
1434 try_next_zone:...1441     } while (*(++z) != NULL);
1442...1448     return page;
1449 }

需要注意的是,即使通过了zone_watermark_ok()的考验,也不一定能够顺利地从buddy system中分配到需要的内存。一个zone有可能空闲页面很多,但是连续的空闲页面不多。

转载于:https://blog.51cto.com/richardguo/1670302

Kernel那些事儿之内存管理(5) --- 衣带渐宽终不悔(上)相关推荐

  1. Kernel那些事儿之内存管理(7) --- Slab(上)

    前面讲的buddy system算法,分配内存的最小单位是一个页面(例如 4K).这对于大的内存申请比较适用.可是实际生活中,Kernel经常需要分配小的内存空间,比如几十个字节,这个时候怎么办呢? ...

  2. Kernel那些事儿之内存管理(8) --- Slab(中)

    上篇讲了Slab中的数据结构,这篇该讲Slab中的操作了. 既然是内存管理,那操作无非就两点:allocate 和 free. 1. 申请一个object 在Slab中,申请一个object是通过函数 ...

  3. Kernel那些事儿之内存管理(2) --- 百闻不如一见

    上次介绍了物理内存管理中三位主要人物中的node 和zone.这两位是当官的,一个是县长,一个是里长,不敢不先介绍啊.接下来出场的就是我们的老百姓了 --- page frame. Page fram ...

  4. Linux学习总结02——内存管理——Linux在X86上的虚拟内存管理

    Linux内存管理之二:Linux在X86上的虚拟内存管理 本文档来自网络,并稍有改动. 前言 Linux支持很多硬件运行平台,常用的有:Intel X86,Alpha,Sparc等.对于不能够通用的 ...

  5. 关于linux内存管理

     Linux的内存管理主要分为两部分:物理地址到虚拟地址的映射,内核内存分配管理(主要基于slab). 物理地址到虚拟地址之间的映射 1.概念 物理地址(physical address) 用于内存芯 ...

  6. 操作系统 内存管理总结

    目录 内存管理介绍 什么是虚拟内存(Virtual Memory)? 逻辑(虚拟)地址和物理地址 CPU 寻址了解吗?为什么需要虚拟地址空间? 局部性原理 操作系统是如何管理虚拟地址与物理地址之间的关 ...

  7. linux chown 将root改变所有者为admin,Linux用户管理 权限管理 内存管理 网络管理命令 (第四天)...

    默认添加的用户会自动加入和用户名一样的组中 su 切换用户 查看当前登陆的用户: whoami id` 查看当前用户属于哪个组:groups groupadd 组名 添加组 groupdel 组名 删 ...

  8. 操作系统第三章-内存管理

    写在前面:本文参考王道论坛的 操作系统考研复习指导单科书 下面的流程图很重要. 加入快表的基本分页 加入快表的二级页表!! 虚拟存储器:请求分页的流程图. 文章目录 第三章 内存管理 3.1 内存管理 ...

  9. 操作系统(3) -- 内存管理

    3.1 内存管理概念 内存管理的基本原理和要求 为什么要进行内存管理? 虽然内存容量在不断增长,但是仍然不可能将所有用户进程和系统所需要的全部程序与数据放入主存,于是操作系统需要对内存空间进行合理的划 ...

最新文章

  1. base cap 分布式_高并发架构系列:详解分布式一致性ACID、CAP、BASE,以及区别
  2. P2278-[HNOI2003]操作系统【堆】
  3. Spring Boot 1:Introduction
  4. 大学刚毕业,零基础大数据如何入门?
  5. vs python opencv_VsCode+Anaconda+OpenCV开发环境搭建
  6. 面试--Linux命令总结
  7. 《跟波利亚学解题》思维笔记
  8. 新手程序员如何找一个靠谱的公司
  9. Filebeat 收集日志的那些事儿
  10. ECMAScript 和 JavaScript 的关系
  11. 四六级英语都考过,让你见识一下“八级程序员”
  12. c++多线程在异常环境下的等待
  13. 世界中英文国家及国家代码
  14. 【Pix4d精品教程】Pix4d空三后处理:点云分类与过滤、DSM精编生成DEM、生成等高线案例详解
  15. U盘因为有写保护,不能格式化,该怎么办
  16. 【springboot】mybatis-generator配置
  17. 2018 PHP面试真题(包括详细解析)
  18. vue 实现 markdown书写博客 showdown
  19. LeetCode题解(1628):设计带解析函数的表达式树(Python)
  20. jwt生成token与解析token

热门文章

  1. 改进我个人知识管理手段
  2. 总结G1垃圾收集器面试题
  3. Pandas入门教程(二)
  4. Java使用OpenCV3.2实现视频读取与播放
  5. C#正则表达式提取HTML中IMG标签中的SRC地址
  6. libgdx的菜单配置,以及json文件的结构
  7. 作为一个php程序员要学会的技能
  8. onCreate()方法中的参数Bundle savedInstanceState 的意义用法
  9. GitHub上最火的40个Android开源项目(二)
  10. EditText自定义边框和背景