浅谈Linux内存管理那些事儿

1 前言

内存管理是Linux内核中很是重要的部分,今天和你们一块儿学习一下。

当咱们要学习一个新知识点时,比较好的过程是先理解出现这个技术点的 背景缘由,同期其余解决方案,新技术点解决了什么问题以及它存在哪些不足和改进之处,这样整个学习过程是 闭环 的,我的以为这是个很好的学习思路。html

凡事都是相通的,计算机学科的一些问题在现实生活中均可以找到原型,因此我以为计算机科学家大部分都是善于观察生活并总结概括的。人类社会就是一台复杂的机器,其中充满了机制和规则,因此有时候跳进代码海洋不如先回到生活之中,寻找原型再探究代码,可能理解会更深入。linux

linux内存管理卷帙浩繁,本文只能层层递进地带你领略冰山轮廓,经过本文你将了解到如下内容:git

  • 为何须要管理内存github

  • linux段页管理机制
    web

  • 内存碎片的产生机理算法

  • 伙伴系统的基本原理
  • 伙伴系统的优点和不足
  • slab分配器的基本原理

2 为何须要管理内存

老子的著名观点是无为而治,简单说就是不过多干预而充分依靠自觉就能够有条不紊地运做,理想是美好的,现实是残酷的。缓存

在linux系统中若是以一种原始简单的方式管理内存是存在一些问题的,咱们来看几个场景。安全

2.1 内存管理的问题性能优化

  • 进程空间隔离问题服务器

假如如今有ABC三个进程运行在linux的内存空间,设定os给进程A分配的地址空间是0-20M 进程B地址空间30-80M,进程C地址空间90-120M,如图:



在某些时候程序空间的访问可能出现问题,好比进程A访问了属于进程B的空间,进程B访问了属于进程C的空间,甚至修改了空间的值,这样就会形成混乱和错误,因此实际中是不容许这种状况发生的。


  • 内存效率和内存不足问题

机器的内存是有限资源,而进程数量是没法肯定的,若是在某些时候已经启动的进程占据了全部内存空间,此时就没法启动新进程了,由于没有新内存可分配了,可是咱们观察到已经启动的进程有时候是在睡大觉,也就是给了内存也不用,这样效率确实是有点低,因此咱们须要一个管理员把不用的内存倒腾出来,另外连续内存实在是很珍贵,不少时候咱们无法有效及时地分配连续内存,所以虚拟化和离散化可能会有效提升内存的使用率。


  • 程序定位调试和编译运行问题

因为程序运行时的位置时不肯定的,咱们在定位问题、调试代码、编译执行时都会存在不少问题,咱们但愿每一个进程有一致且完整的地址空间,一样的起始位置放置了堆、栈以及代码段等,从而简化编译和执行过程当中的 linker 连接器、loader 加载器的使用。

2.2 虚拟地址空间

为了解决上述的一些问题,linux系统引入了虚拟空间的概念,虚拟化的出现和硬件有密不可分的联系,能够说是软硬件组合的结果,虚拟地址空间就是在程序和物理空间所增长的中间层,这也是内存管理的重点。


磁盘 disk 做为一种大容量的存储也做为"内存"的一部分参与程序的运行,内存管理系统会将不经常使用非活跃内存进行页面换出,能够认为内存磁盘的缓存,内存中保留了活跃的数据,从而间接扩展了有限的物理内存空间,这部分空间称为虚拟内存是相对于物理内存而言的。



3.段页管理机制

本文并不深刻地将分段管理内存和分页管理内存,由于将这些细节的优秀文章不少,感兴趣的使用搜索引擎一键即达。

段页机制也不是一蹴而就的,经历了单纯物理分段、单纯分页、单纯逻辑分段等阶段,最终演进出来了分段和分页结合的内存管理方式,段页结合得到了分段和分页的优点也避免了单一模式的弊端,是一种比较好的管理模式。

本文对于段页管理机制只想通俗地说明一些概念,段页管理机制是分段式管理和分页式管理的组合,段式管理是逻辑上的管理方式分页管理是偏物理上的管理方式

计算机里面的一些技术和实现均可以在现实生活中找到缩影,所谓艺术和科技源自生活大概就是这个意思吧。

举个栗子
在进行居民户籍管理时都会有区县市的概念,可是实际上并无这种实体,都是逻辑上的,增长了这些行政单位以后可让地址管理更加直接。

对于咱们居民来讲惟一的实体就是本身的房子住所,这是物理上的单位,是真实存在的,这也是最基本的单位。

对比linux段页时管理来讲,段是逻辑上的单位至关于区县市的概念,页是物理上的单位至关于小区/房屋的概念,这样就方便不少。

多级页表也很好理解,总的物理内存假若有4GB,页大小为4KB,那么就总共有2^20个页,数量仍是很是大的,这样编号来创建索引寻址比较不方便,因此引入多级页表,来减小存储便于管理。

段页机制加持下的逻辑地址和物理地址的映射关系简图,也就是虚拟地址到物理地址的对应关系:

图片来自网络

内存管理单元( MMU Memory Management Unit )是硬件层组件,主要提供将虚拟地址映射为物理地址。

MMU 的工做流程:CPU 生成逻辑地址交给分段单元,分段单元进行处理将逻辑地址转换为线性地址,再线性地址交给分页单元,分页单元根据页表映射转换内存物理地址,其中可能出现缺页中断。

缺页中断( Page Fault )是只当软件试图访问一个虚拟地址时,通过段页转换为物理地址以后,此时发现该页并无在内存中,这时 cpu 就会报出中断,再进行相关虚拟内存的调入工做或者分配工做,若是出现异常也可能直接中断。

4.物理内存和内存碎片

前面说的段页管理机制算是虚拟空间的部分,然而linux内存管理的另一个重要部分就是物理内存的管理了,也就是如何分配和回收物理内存,这就涉及到一些内存分配算法和分配器

4.1 物理内存分配器

分配器和分配算法就像公司财务内存就像公司资金,如何把资金合理使用是财务的本职工做,如何把物理内存合理使用是分配器的份内之事。

4.2 内存碎片分类和机理

若是咱们不知道内存碎片是什么,试想一下咱们常说的碎片化的时间,也就是那些虽然空闲可是没有被利用的时间,其实内存也是如此。

不管是时间仍是内存被碎片化以后都没法被有效利用,所以合理管理减小碎片对咱们来讲是相当重要的,这也是物理内存分配算法和分配器的研究重点。

按照碎片的位置和产生缘由,内存碎片分为外部碎片内部碎片,咱们看下这两种碎片的直观展现:



图片来自网络

从图中能够知道,外部碎片是进程与进程间未分配的内存空间,外部碎片的出现和进程频繁的分配和释放内存有直接关系,这个很好理解,模拟一下分配不一样空间的进程不一样时间释放就能够看到外部碎片的产生了。


内部碎片主要由于分配器粒度问题以及一些地址限制致使实际分配的内存大于所需内存,这样在进程内部就会出现内存空洞


虽然虚拟地址让进程使用的内存在物理内存上是离散的,可是不少时候进程须要必定量连续物理内存,若是大量碎片存在,就会形成没法启动进程的问题,如图Process7须要一块连续的物理内存却没法被分配:

图片来自网络

若是仍是没有很清楚,试想一下平时和三五好友去食堂吃饭或者坐公交的场景,整个车箱都没有连续的3个座位,因此要么分开坐要么都站着

5. 伙伴系统算法基本原理

5.1 一些准备知识

  • 物理页框

linux将物理内存按照页来划分,内存页的大小在不一样的软硬件中可能不同,linux内核设置为4KB,有的内核可能更大也可能更小,当时不一样的大小在实际中都是有考量的,就像面包同样有大有小,并非整齐划一的。

  • 页框记录结构

在内核中为了创建对物理内存页page的使用状况的监控,会有struct page这样的数据结构来记录页的位置地址/使用状况等,至关于内核对内存页管理的一本帐目。

  • 延时分配和实时分配

linux系统有内核态和用户态之分,内核态申请内存就马上知足而且认为这个请求必定是合理的。然而用户态申请内存的请求,老是尽可能延后分配物理内存,因此用户态进程是先得到一个虚拟内存区,在运行时经过缺页异常得到一块真正的物理内存,咱们执行 malloc 时获取的只是虚拟内存而已,并非真实的物理内存,也是这个缘由形成的。

5.2 伙伴系统简介

第一次听到这个算法名称就很好奇为何叫伙伴系统?让咱们来一块儿揭秘。

  • 伙伴系统要解决什么问题

伙伴系统算法是解决外部碎片的有力工具,简单来讲它针对频繁请求和释放不一样大小的一组连续页框的场景,创建一套管理机制来高效的分配和回收资源,下降外部碎片。

  • 解决外部碎片的思路

第一种思路:把已经存在的外部碎片经过新的技术把这些非连续的空闲内存映射到连续的线性空间,其实至关于没有去下降外部碎片的产生而是治理型方案,可是这种方案在真实须要连续物理内存时是无效的。

第二种思路:把这些小的空闲的不连续内存记录在案,若是有新的分配需求就从中搜索合适的将空闲内存分配出去,这样就避免了在新的区域进行分配内存,有种变废为宝的感受,其实这样场景也很熟悉当你想吃一包饼干时,你妈妈确定会说先把以前剩下一半没吃完的吃掉,不要先开新的了。

基于一些其余方面的考量,linux内核选择了第二种思路来解决外部碎片。

  • 伙伴内存块的定义

在伙伴系统中把大小相同且物理地址连续的两块内存区域称为伙伴,连续地址的要求实际上是比较苛刻了,可是这也是算法的关键,由于这样的两块内存区域能够合并一块更大的区域。


  • 伙伴系统的核心思想

伙伴系统将不一样大小的连续物理页框进行管理,在申请时从最接近的页框大小进行分配,剩余的进行新的拆解,并将有伙伴关系的内存会进行合并成为大的页框。

5.3 伙伴系统的基本过程

伙伴系统维护了 n=0~10 共 11 个块链表,每一个块链表分别包含了大小为 2^n 个连续的物理页。当 n=10 时即 1024 个 4KB 页对应 4MB 大小的连续物理内存块,这里的 n称为 order,在伙伴系统中 order为0~10,也就是最小的是 4KB,最大的内存块是4MB,这些相同大小的物理块组成双向链表进行管理,如图展现了 order=0 和 order=2 的两个双向链表的状况:

图片来自网络


申请内存过程:假设请求一个页框块,伙伴系统算法先在 order=0 的链表中检查是否有空闲块可分配。若是没有则查找下一个更大的块,在 order=1 的链表中找一个空闲块,链表中存在就把2个页框拆分,1个页框分配出去1 个页框加入到 order=0的链表中。若是 order=1 的链表中仍未找到空闲块,就继续向更大的order搜索,若是找到进行拆分处理,若是最终至 order=10 的链表也没有空闲块,则算法报错。

合并内存过程:合并内存的过程是伙伴算法中伙伴块的体现,算法把两个块具备相同大小 A且它们物理地址连续的内存合并为一个大小为 2A 的单独块。伙伴算法是自底向上迭代合并的,其实这个过程和 leveldb 中 sst 的合并过程很类似,区别在于伙伴算法要求内存块是连续的,这个过程也体现了伙伴系统对于大块内存的友好。

图片来自网络

5.4 伙伴系统的优点和不足

伙伴系统算法较好地解决了外部碎片问题,而且对于大内存块的分配比较友好小粒度的内存可能形成内部碎片,可是伙伴系统对于伙伴块的定义很苛刻,而且在合并伙伴块的过程涉及较多的链表操做,对于一些频繁的申请可能刚合并就会被拆分掉,这就作了无用功,因此伙伴系统仍是存在一些问题的。

6. Slab分配器

从伙伴系统的介绍能够知道其分配的最小单位是 4KB 的页框,这对于一些频繁申请的小到几十字节的内存来讲仍是十分浪费的,因此咱们须要更细粒度的分配器,这就是slab分配器。

slab分配器并非和伙伴系统分立的,而是创建在伙伴系统之上,能够看做是伙伴系统的二级分销商,更加靠近用户侧,可是slab分配器由于更靠近使用方,所以在结构实现上比伙伴系统更加复杂,本文只能简单归纳。

我的感受slab分配器的亮点包括:最小粒度为对象和内存惰性归还

Linux 所使用的 slab 分配器的基础是 Jeff Bonwick 为 SunOS 操做系统首次引入的一种算法。Jeff 的分配器是围绕对象缓存进行的。在内核中,会为有限的对象集(例如文件描述符和其余常见结构)分配大量内存。Jeff 发现对内核中普通对象进行初始化所需的时间超过了对其进行分配和释放所需的时间。所以他的结论是不该该将内存释放回一个全局的内存池,而是将内存保持为针对特定初始化的状态。

from 《linux slab 分配器剖析》

slab采用对象做为最小单位的理论基础就在于初始化一个结构的时间可能超过了分配和释放的时间

slab分配器能够看做是一种内存预分配机制,就像超市会把经常使用的物品放到你们更容易找到的位置,事先把这些对象准备好申请时就能够马上分配出去了。

  • slabs_full链表中slab已经彻底分配出去

  • slabs_partial链表中的slab部分已经被分配出去了

  • slabs_empty: 链表中的slab都是空闲的 也就是能够被回收

对象是从 slab 中进行分配和释放的,每一个kmem_cache的slab列表是存在状态迁移的,可是被回收的部分slab并不会马上归还给伙伴系统,而且在分配时会优先分配最近被释放的对象,目的是利用cpu缓存的局部性原理,能够看出来slab分配器的细节作的很足,可是为了实现这一套复杂的逻辑,就要维护多个队列会比伙伴系统更复杂。

slab的内容相比buddy更复杂,本文再也不展开。

7.结语

linux内存管理的东西确实很是多,本文也只有5k字而且没有源码,因此只能是浅谈

从工程角度来讲,本文也只是抛砖引玉的做用,因此这篇文章并非深刻的探讨内存管理,对此我也表示歉意。

让个人好朋友sy审稿了一下,他说看着写了不少但又好像啥也没写,其实我跟他的感觉同样,可能最近写文章没感受。

因此最后仍是那句话,本文只是浅谈,深刻理解仍是须要去看内核书籍,没有捷径。

8.巨人的肩膀

  • https://blog.csdn.net/XD_hebuters/article/details/79519406

  • https://blog.csdn.net/xd_hebuters/article/details/79506062

  • https://jacktang816.github.io/post/linuxmemorymanage/

  • https://jacktang816.github.io/post/memoryfragmentation/

  • https://www.jianshu.com/p/98f9f86b2aeb

  • https://www.cnblogs.com/sunsky303/p/9214223.html

  • https://www.cnblogs.com/klb561/p/11062166.html

  • http://abcdxyzk.github.io/blog/2015/03/03/kernel-mm-slab2/

  • https://www.ibm.com/developerworks/cn/linux/l-linux-slab-allocator/index.html



后台回复“加群”,带你进入高手如云交流群


推荐阅读:

一张图带你按部就班掌握性能优化问题

三张图完全搞懂iptables和netfilter

救命,Linux正在吃掉个人内存!

别再说你不懂Linux内存管理了

强烈安利的几款画图工具

LVS 实战抓包分析

故障排查:K8s中Pod没法正常解析域名

详解Linux中3个文件查找相关命令

网络排错大讲解~

如何诊断 Linux 服务器的性能?

用了HTTPS就安全了吗?

HTTP/3 原理实战

Linux 下10个帮助你调试的命令

OVS 和 OVS-DPDK 对比

微软出品的最新K8S学习指南3.0下载

了解HTTPS工做原理,看这一篇就够了



喜欢,就给我一个“在看”



10T 技术资源大放送!包括但不限于:云计算、虚拟化、微服务、大数据、网络、Linux、Docker、Kubernetes、Python、Go、C/C++、Shell、PPT 等。在公众号内回复「1024,便可免费获取!!

本文分享自微信公众号 - Linux云计算网络(cloud_dev)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索