本文介绍内核如何给自己分配物理内存并管理。对应《深入》第8章。

在《深入》第2章“内存寻址”(或者是我博客中的这篇文章,点这里)中,已经介绍了内核如何给自己分配1G的线性地址的。但是物理内存的分配及管理恐怕更复杂而且更有必要:内存分配和释放的速度(由内存管理的算法决定)影响内核的工作效率。

首先介绍概念:

页框:通俗的说就是物理内存上的单元。请注意与“页”的区别。页有两重意思,不过最多的用法是指线性地址的单元。所以一个是实际物理内存的单元,一个是线性地址/虚拟内存的单元。在大多数情况下,页框大小等于页的大小,为4KB,使得一个页框恰好可以容纳一个页的数据。

(1)为什么要进行内存管理?

内存管理的目的总体上说无非是两点:提高时间效率和提高空间效率。时间效率也就是尽量使寻找到空闲内存、分配和释放这块内存的时间更短。空间效率就是指尽量能找到合适大小的空间,并减少内存空间浪费。

关于空间效率,举个简单的例子:切蛋糕。当我们拿到一块完整的蛋糕的话,如果想每个人都吃到完整的一块,那么我们当然不能没有计划的切块。虽然总量是不变的,但是如果随便乱切,横七竖八,势必导致最后剩下的蛋糕都是碎块,那么后面吃蛋糕的同学必然只能把小块小块的蛋糕凑成一大块来吃。势必很不爽啊。。。

(2)关键数据结构:页描述符

如果需要对一个东西进行管理,那么必须要有负责管理的数据结构,这个数据结构中有各个字段,用来提供不同的管理功能。举例子:比如为了保护内核的内存不被用户进程使用,就必须使用一个标志位;用户进程在读写内存时,首先就要检查这个标志位,然后才能读写。

所以内核使用了“页描述符”这个数据结构,页描述符的类型是page,长度为32字节(是字节哦),所有的页描述符放在一个数组mem_map中。我个人觉得好像应该叫“页框描述符”比较好。

(3)为什么要使用内存管理区?

内存本来在物理上是一个实体,并不分区的。有人会问:这样的一个整体进行处理不是很简洁么?为什么要分区呢?

但是由于以下的两种约束,我们迫不得已给自己增加了负担,将内存分区来管理。

约束一:DMA。这个名词不再解释,请自行google。在进行DMA数据传输时,DMA控制器只能对16M的内存进行寻址,所以被迫无奈,只好将这16M的物理内存固定划分给DMA了。

约束二:线性地址有限,无法直接大的物理内存。所以,超出896M的物理内存必须被区别对待。

所以,最终,物理内存被分配成3个管理区:ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM。

(4)为什么要使用伙伴系统(Buddy system)算法?

使用伙伴系统的直接目的就是为了防止物理内存“外碎片”的产生。关于外碎片,简而言之就是虽然我们一直分配或者释放连续的内存,但是结果就是我们的内存变得很不连续,充满了大大小小的碎片或者是洞,以至于我们无法再分配足够大的一块连续的内存。(回忆下我们刚才说过的蛋糕的例子)

下面举例说明。假设我们要分配一块256个连续页框的内存。

首先考虑一种不好的分配算法作为对比,例如:将所有的空闲连续页框链在一个链表中,我们在分配空间时选取遇到的第一个大于256个连续页框(这个可能很随机,可能是1024个页框,也可能是512个,也可能恰好是256个;而且我们不能奢望在链表的开头遇到大于256个的连续页框,如果链表很长。。。Orz)。假设我们遇到了空闲的1024个页框。这时候我们面临两个选择:

(a)把这1024个页框都分配出去。这显然是一种巨大的浪费。

(b)把这1024个页框切开,将256个连续页框脱离链表,然后链表上剩下了768个连续页框的块。这其实和外碎片没有什么区别(因为链表后面很可能有更适合256的连续页框,比如257个或者258个连续页框),最终会导致这个链表被分割的乱七八糟,以至于再也找不到一块合适的连续空间。

(5)伙伴系统

所以,Linux使用了著名的伙伴系统来处理这个问题。那么下面用实例说明伙伴系统是如何改进的。最后我们再总结一下伙伴系统的思想。

伙伴系统的基础是11个连续页框链表,第一个链表上只存储所有空闲的1个页框,第二个链表上只储存所有空闲的2个连续页框,以此类推。这11个链表将不同大小的连续内存块链在不同的链表上,这首先节省了遍历一个很长的链表的时间,可是使我们直奔最符合需求的连续内存。如果是需要256个连续页框,我们可以直接找到第9个链表,取出一个即可。但又出现了几个问题:(q1)如果我们需要254个连续页框呢?(q2)如果我们已经没有了256个的连续页框,即256个连续页框的链表已经空了呢?

下面介绍如何妥善处理剩余部分,也就是解决问题q1。我们只需要254个连续页框时,我们把这个256个连续页框从链表上切除,但是只分配出去254个页框,剩余的两个连续页框链入属于它的链表,也就是存储所有两个连续页框的链表。这样我们看到,我们一个页框都没有浪费。

有人可能会问,如果需要258个连续页框怎么办(258>256)?这个问题其实与q1等价,但是此时最符合需求的是512个连续页框,但是剩下的部分只是要切割多次。但我们无需证明最后一个页框都不会浪费(因为至少最后都会链到1个页框的链表里)。q2其实与这种情况很类似,仍然要去更大的一级链表中寻找。

那么伙伴系统与开始介绍的那种不好的算法到底有什么本质区别呢?

简而言之,伙伴系统直奔最符合需求的连续内存,然后它妥善的处理了剩余部分。首先我们使用了空间换时间的算法:维护这11个块链表的开销,相对于一个好的内存分配算法根本算不上什么。其次,伙伴系统是类似“贪心”的。它寻找当前认为最好的。最后,它把分配后剩余的部分妥善的移动到它们最应该去的地方。

(6)使用的函数

分配:alloc_pages()/alloc_page()/__alloc_pages()

释放:__free_pages()/free_pages()/free_page()

==============================

下面插入两个概念,内存区(memory area)与内碎片。内存区这个词很容易让人产生误会,它不是指整个内存,而是指连续的任意长度的物理内存区。而内碎片是相对于外碎片讲的,外碎片是以页框为单位的,在页框之外;而内碎片就是在页框内部的碎片。产生内碎片的原因其实与外碎片一致:虽然我们所有的分配和释放都是连续的,但最后结果却是不连续的物理内存。

===============================

(7)为什么要使用slab分配器?

我们先不管slab是什么,先考虑为什么要使用它:它的出现就是为了解决内碎片的问题。

当然,有人肯定会想,使用伙伴系统解决内碎片不是一样么?只不过把单位从页框改为字节。

早期Linux确实是这么做的。但是遇到了一些问题(详见《深入》p.324),以我的理解就是:当分配和释放的粒度变细时,被迫需要考虑更多的因素,比如说需要考虑到数据类型。所以伙伴系统在处理内碎片时效率并不是最好的。

于是采用了具有面向对象思想(考虑到数据类型,就已经是一种面向对象的思想了)的slab系统。

(8)slab系统的思想的。。。呃 猜测

这里,限于篇幅和个人能力有限,就不详细介绍slab系统了。我的理解是:slab系统提供了一种快速查找并分配特定类型的数据(面向对象)的细粒度连续内存空间的方法。因为它使用了下图所示的这种层级结构,让我不由得联想到了数据结构中的B树(B树也是用来快速查找,减少树的层数的,当然也付出了一定的空间复杂度;当然我还不知道我这种联想合理不合理)。

(9)slab所使用的函数

分配和释放slab:kmem_cache_alloc()/kmem_cache_free()

分配和释放对象:kmalloc()/kfree()。

======================================

参考资料:

这里有一篇IBM关于slab的技术文章:Linux slab 分配器剖析

转载于:https://www.cnblogs.com/microgrape/archive/2011/05/12/2043909.html

《深入理解Linux内核》笔记5:内存管理相关推荐

  1. linux内核工程导论,Linux内核工程导论——内存管理(3)

    Linux内核工程导论--内存管理(三) 用户端内核内存参数调整 /proc/sys/vm/ (需要根据内核版本调整) 交换相关 swap_token_timeout Thisfile contain ...

  2. 详细讲解Linux内核源码内存管理(值得收藏)

    Linux的内存管理是一个非常复杂的过程,主要分成两个大的部分:内核的内存管理和进程虚拟内存.内核的内存管理是Linux内存管理的核心,所以我们先对内核的内存管理进行简介. 一.物理内存模型 物理内存 ...

  3. Linux内核初始化阶段内存管理的几种阶段

    本系列旨在讲述从引导到完全建立内存管理体系过程中,内核对内存管理所经历的几种状态.阅读本系列前,建议先阅读memblock的相关文章. 一些讲在前面的话 在很久很久以前,linux内核还是支持直接从磁 ...

  4. 深入理解Linux内核01:内存寻址

    目录 1. 内存地址 1.1 三种地址 1.1.1 逻辑地址(logical address) 1.1.2 线性地址(linear address) 1.1.3 物理地址(physical addre ...

  5. epub 深入linux内核架构_深入分析Linux内核源代码6-Linux 内存管理(2)

    每天十五分钟,熟读一个技术点,水滴石穿,一切只为渴望更优秀的你! ----零声学院 6.3 内存的分配和回收 在内存初始化完成以后,内存中就常驻有内核映像(内核代码和数据).以后,随着用 户程序的执行 ...

  6. linux异构内存,Linux内核添加异构内存管理(HMM)将带来加速GPU的新方式,还有可能带来其他类型的机器学习硬件。...

    一项旨在让机器学习或其他基于GPU的应用得以大幅提升性能的内存管理功能已开发了很长一段时间,不过现在它即将进入到Linux内核的某下一个版本中. 异构内存管理(HMM)让设备的驱动程序可以为受制于自身 ...

  7. [十月往昔]——Linux内核中的内存管理浅谈

    为什么要叫做"十月往昔"呢,是为了纪念我的原博客,http://www.casual0402.cn. 不知道为什么,突然想来一个新的开始--而那个博客存活至今刚好十个月,也有十个月 ...

  8. Linux内核机制总结内存管理之页表缓存(十九)

    文章目录 1 页表缓存 1.1 TLB表项格式 1.2 TLB管理 1.3 地址空间标识符 1.4 虚拟机标识符 重要:本系列文章内容摘自<Linux内核深度解析>基于ARM64架构的Li ...

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

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

  10. Linux内核剖析 之 内存管理

    1. 内存管理区 为什么分成不同的内存管理区? ISA总线的DMA处理器有严格的限制:仅仅能对物理内存前16M寻址. 内核线性地址空间仅仅有1G,CPU不能直接訪问全部的物理内存. ZONE_DMA  ...

最新文章

  1. Linux下多播的配置【十全十美】
  2. HTTP的长链接和短链接说明
  3. SharePoint网站集备份与恢复
  4. CVPR2014: DeepID解读
  5. MIK C语言面试两题
  6. ubuntu指向python3_ubuntu安装python3.7,并更新python默认指向为python3.7
  7. 应力偏张量的物理意义_物理学中张量的由来
  8. MFC去掉窗口右上方最大化最小化关闭按钮
  9. matlab中一个显示根号的技巧
  10. 网页中图片无法显示解决方法
  11. php执行fastlane,Fastlane的使用及进阶
  12. python 计算list中平均值_python里的list求平均值
  13. LabVIEW如何减少下一代测试系统中的硬件过时6
  14. 在线文档查看器:Gleamtech Document Viewer 6.6.1
  15. win7笔记本网络连接图标一直转圈但可上网
  16. 淘宝百亿补贴的发展及日后玩法应对策略[淘宝子评价查询接口]
  17. 【转】设置右键显示/隐藏系统文件
  18. Python 中MNE库去伪迹(ICA)案例的逐句解析
  19. unity_Text文本和图片组合
  20. 页面置换算法最佳页面置换算法模拟JAVA实现

热门文章

  1. 解释性语言和汇编性语言对比
  2. 运算符与,|与||的区别
  3. 如何在 Centos7 x86_64下将vim一键配置为一款强大的C++,IDE
  4. Insertion Sort——打表找规律
  5. 【汇编语言】结合C语言,使用VS 2017调试模式下的反汇编工具学习32位x86汇编指令
  6. 线程间通信————同步
  7. gdb调试(如何跟踪指定进程)
  8. malloc/free 和 new/delete
  9. ES6标准入门(第二版)pdf
  10. 第一章-从双向链表学习设计