内存管理

linux实践

命令:free -m命令以MB为单位显示内存使用情况。

avformat_find_stream_info 内存限制_linux

total:总计物理内存的大小。

used:已使用多大。

free:可用有多少。

Shared:多个进程共享的内存总额。

Buffers/cached:磁盘缓存的大小。(buffer 是缓冲区,cache是页高速缓存。无论是缓冲区还是页高速缓存,它们的实现方式都是一样的。缓冲区只不过是一种概念上比较特殊的页高速缓存罢了)

available还可以被 应用程序使用的物理内存大小

SWAP是交换分区,也就是我们通常所说的虚拟内存(当我们内存不够用的时候就会用到这个SWAP),而当系统需要访问 swap 上存储的内容时,再将 swap 上的数据加载到内存中,这就是常说的换出和换入

free 与 available 的区别

或者 可参考这篇文章

free真正尚未被使用的物理内存数量。
available应用程序认为可用内存数量,available = free + buffer + cache (注:只是大概的计算方法)

Linux 为了提升读写性能,会消耗一部分内存资源缓存磁盘数据,对于内核来说,buffer 和 cache 其实都属于已经被使用的内存。但当应用程序申请内存时,如果 free 内存不够,内核就会回收 buffer 和 cache 的内存来满足应用程序的请求。这就是稍后要说明的 buffer 和 cache。

基本概念

  1. 静态重定位是在程序执行之前进行重定位,它根据装配模块将要装入的内存起始位置,直接修改装配模块中的有关使用地址的指令。
    静态重定位有着无需硬件支持的优点,但存在着如下的缺点:一是程序重定位之后就不能在内存中搬动了;二是要求程序的存储空间是连续的,不能把程序放在若干个不连续的区域内。
  2. 动态重定位是指,不是在程序执行之前而是在程序执行过程中进行地址重定位。更确切地说,是在CPU每次访问内存单元前才进行地址变换。
    优点
  1. 目标模块装入内存时无需任何修改,因而装入之后再搬迁也不会影响其正确执行,这对于存储器紧缩、解决碎片问题是极其有利的;
  1. 一个程序由若干个相对独立的目标模块组成时,每个目标模块各装入一个存储区域,这些存储区域可以不是顺序相邻的,只要各个模块有自己对应的定位寄存器就行。

**缺点:**需要硬件支持。

  1. 物理内存分为固定大小的块,称为
  2. 逻辑内存也分为同样大小的块,称为。分页支持一直都是由硬件来处理的。

内存扩充

https://www.zhihu.com/question/21088377 参考博文

交换技术

有两种处理内存超载的通用方法:交换和虚拟内存

交换,就是当一个程序运行完,就立刻从内存中换出到磁盘,然后要用的时候再换入到内存,这里面的换入,会用到基址寄存器和界限寄存器来给程序进行动态重定位

交换的时候会产生多个空闲区,我们把这些小的空闲区合成一大块,这个技术就叫内存紧缩(通常是不进行这个操作,因为会耗费大量CPU时间)

虚拟内存,在上面那篇参考博文已经解释得非常清楚了,我就不重复了!

内碎片

内存固定分区的时候,出现分配的分区大于所需要的内存空间

外碎片

动态分区,内存中存在很多很小的空闲快,他们每一个都很小不足以满足分配要求,但其综合满足分配要求。

无存储器抽象

  1. 每个程序能直接访问物理内存,在这个系统中时根本没有多进程的,因为两个进程同时运行的时候,一个进程会把另一个进程所写的内容覆盖掉,所以实现并行的一种方法是使用多线程编程。
  2. 在不使用存储器抽象的情况下运行多个程序,就是把每个程序读到内存,把当前内存中的所有内容保存到磁盘文件,然后再拿下一个程序出来再运行即可。
  3. 这些方法最大问题就是,多个程序运行的时候,都是引用了绝对物理地址。补救方法:在第二个程序装载进内存的时候,使用静态重定位(大大减慢装载速度)的技术修改它

一种存储器抽象:地址空间

主要功能:为了避免用户直接操作物理地址。为程序创造了一种抽象的内存

  1. 解决多程序同时处于内存,且相互不影响的两个解决方法是:保护和重定位
  2. 但是我们很难给每个程序分配一个独有的地址空间

基址寄存器与界限寄存器

这两个寄存器就是为了解决上面那个困难的,这个方法就是动态重定位(简单地把每个进程的地址空间映射到物理内存的不同部分),也就是说使用动态重定位,每个进程的地址空间是可以重复的

缺点:每次访问内存都要进行运算,导致访问时间变长

存储管理的主要任务

  1. 内存分配和回收:由操作系统完成主存储器空间的分配和管理,使程序员摆脱存储分配的麻烦,提高编程效率。
  2. 地址变换:在多道程序环境下,程序中的逻辑地址与内存中的物理地址不可能一致,因此存储管理必须提供地址变换功能,把逻辑地址转换成相应的物理地址
  3. 内存信息的共享和保护
    上下界保护法

存储管理方案

分区式存储管理
  1. 分区式存储管理,他也是一种连续内存分配方式,把内存分为一些大小相等或不等的分区,操作系统占用其中一个分区,其余的分区由应用程序使用,每个应用程序占用一个或几个分区。
  1. 单一连续存储管理,内存被分为两个区域:系统区和用户区。其特点是,最简单,适用于单用户、单任务的操作系统。这种方式的最大优点就是易于管理。但也存在着一些问题和不足之处,例如对要求内存空间少的程序,造成内存浪费;程序全部装入,使得很少使用的程序部分也占用—定数量的内存。只支持单道程序设计
  2. **固定分区 **,将内存划分成若干个固定大小的块,分区大小可以相等也可不相等(划分之后不再改变)。
    优点:易于实现,开销小。
    缺点:主要有两个,内碎片造成浪费;分区总数固定,限制了并发执行的程序数目。
  3. 可变分区,动态分区的特点是动态创建分区:在装入程序时按其初始要求分配,或在其执行过程中通过系统调用进行分配或改变分区大小。其优点是:没有内碎片
    但它却引入了另一种碎片—>外碎片

系统要使用什么样的数据结构来记录内存的使用情况

  1. 空闲分区链(表)(大小 ,起址,状态)。
    动态分区分配的实现:一个空闲表或者空闲链来记录目前系统中空间的内存区域。在内存分配时,需要查找空闲表或空闲链找到一块内存分配给当前进程。
    空闲分区表

空闲分区链

avformat_find_stream_info 内存限制_页表_02

  1. 使用位图
    内存的大小和分配单元的大小决定了位图的大小,所以它提供了一种简单的利用一块固定大小的内存区就能对内存的使用情况进行记录,缺点就是,找出k个连续0的串是耗时的操作

    avformat_find_stream_info 内存限制_页表_03

分配算法

  1. 首次适配法:按分区在内存的先后次序从头查找,找到符合要求的第一个分区进行分配。该算法的分配和释放的时间性能较好,较大的空闲分区可以被保留在内存高端。但随着低端分区不断划分会产生较多小分区,每次分配时查找时间开销便会增大。
  2. 循环首次适应算法不再是每次都从链首开始查找,而是从上次找到的空闲分区的下一个空闲分区开始查找,直至找到一个能满足要求的空闲分区,从中划出一块与请求大小相等的内存空间分配给进程。
  3. 最坏适配法:按分区在内存的先后次序从头查找找到最大的空闲分区进行分配。基本不留下小空闲分区不易形成外碎片。但由于较大的空闲分区不被保留,当对内存需求较大的进程需要运行时,其要求不易被满足。
  4. 最佳适配法:按分区在内存的先后次序从头查找,找到其大小与要求相差最小的空闲分区进行分配。从个别来看,外碎片较小;但从整体来看,会形成较多外碎片优点是较大的空闲分区可以被保留
  5. 快速适应算法:每一类具有相同容量的空闲区设立一个空闲分区链表,每个链表的表头指针放在一个索引表中。

可变分区内存回收(使用链表管理

合并相邻空闲区

假设,A地址是低地址,阴影部分是空闲区

avformat_find_stream_info 内存限制_页表_04

  1. d情况,若释放区X既有上邻空闲区,又有下邻空闲区。将三个空闲区合并成一个大空闲区.先将X与B合并记为B,
    再将B与A合并记为A,并将B从链中删除
  2. c情况,若释放区x只有上邻空闲区B,则只修改空闲区B大小即可
  3. b情况,只有下邻空闲区**,修改空闲区B的首地址**
  4. a情况,既无上邻又无下邻空闲区,修改释放区的首地址为空闲区的起始地址
伙伴系统

Linux采用伙伴系统解决外部碎片的问题,监视内存,保证在内核只要申请一小块内存的情况下,不会从大块的连续空闲内存截取一段过来,从而保证了大块内存的连续性和完整性。

伙伴系统的宗旨就是用最小的内存块来满足内核的对于内存的请求

avformat_find_stream_info 内存限制_物理地址_05

A先请求100KB,然后在1M中分成了两个512KB的内存块,一个分配给应用,一个是空闲,然后在分配给应用的512KB再分一半,分成了两个256KB,也是一样,一个分配给应用,一个是空闲,发现还可以再分,就把256kb一样分两份,最后发现不能分了,就不分了。页框块在释放时,会主动将两个连续的页框块合并成一个较大的页框块。

伙伴系统的一个优点是:通过称为合并的技术,可以将相邻伙伴快速组合以形成更大分段

伙伴系统的明显缺点是:

  1. 由于圆整到下一个 2 的幂,很可能造成分配段内的碎片。例如,33KB 的内存请求只能使用 64KB 段来满足。事实上,我们不能保证因内部碎片而浪费的单元一定少于 50%。
  2. 一个很小的块往往会阻碍一个大块的合并,一个系统中,对内存块的分配,大小是随机的,一片内存中仅一个小的内存块没有释放,旁边两个大的就不能合并。
  3. 拆分和合并涉及到较多的链表和位图操作,开销还是比较大的
slab分配

每个 slab 由一个或多个物理连续的页面组成,每个 cache 由一个或多个 slab 组成,每个内核数据结构都有一个 cache。slab 分配算法采用 cache 来存储内核对象

avformat_find_stream_info 内存限制_操作系统_06

lab 分配器提供两个主要优点:

  1. 没有因碎片而引起内存浪费。碎片不是问题,因为每个内核数据结构都有关联的 cache,每个 cache 都由一个或多个 slab 组成,而 slab 按所表示对象的大小来分块。因此,当内核请求对象内存时,slab 分配器可以返回刚好表示对象的所需内存。
  2. 可以快速满足内存请求。因此,当对象频繁地被分配和释放时,如来自内核请求的情况,slab 分配方案在管理内存时特别有效。分配和释放内存的动作可能是一个耗时过程。然而,由于对象已预先创建,因此可以从 cache 中快速分配。再者,当内核用完对象并释放它时,它被标记为空闲并返回到 cache,从而立即可用于后续的内核请求。
页式存储管理

https://zhuanlan.zhihu.com/p/37549063 这里推荐这篇文章

avformat_find_stream_info 内存限制_内核_07

CPU生成的每个地址分为两个部分:页号和页偏移页号作为页表索引页表包含每页所在物理内存的基地址

  1. 逻辑地址到物理地址的变换过程
  1. 进程访问某个逻辑地址时,分页地址机构自动将逻辑地址分为页号和页内地址
  2. 页号大于页表长度,越界错误
  3. 页表项的地址 p = 页表起始地址 F + 页号 P * 表项大小 S,从而得到对应的物理块号 B
  4. 页和物理块的大小是一致的,所以 页内地址=块内地址
  5. 然后 物理地址 = 物理块号 B * 页大小 L + 页内地址
  6. 根据物理地址读取数据

以上可能会是一道大题,记住公式:

页号 P=逻辑地址/页大小

页内地址 F=逻辑地址%页大小

物理地址 A=物理块号*页大小 + 页内地址

  1. 页表/快表(TLB,Translation Look aside Buffer)
  1. 快表是为了加快虚拟地址到物理地址这个转换过程而存在的。快表一般存放在TLB(TLB是一个内存管理单元用于改进虚拟地址到物理地址转换速度的缓存)。快表与页表的功能类似,其实就是将一部分页表存到 CPU 内部的高速缓冲存储器 Cache。CPU 寻址时先到快表查询相应的页表项形成物理地址,如果查询不到,则到内存中查询并将对应页表项调入到快表中。但,如果快表的存储空间已满,则需要通过算法找到一个暂时不再需要的页表项,将它换出内存。因为高速缓冲存储器的访问速度要比内存的访问速度快很多,因此使用可以大大加快虚拟地址转换成物理地址。根据统计,快表的命中率可以达到 90%以上。

一道关于块表的题目

avformat_find_stream_info 内存限制_页表_08

  1. 一级页表的缺陷,由于页表必须连续存放,并且需要常驻物理内存,当逻辑地址空间很大时,导致页表占用内存空间很大。采用离散分配方式的管理页表,将当前需要的部分页表项调入内存,其余的页表项仍驻留在磁盘上,需要时再调入。(按需调入)
  2. 二级页表,二级页表即是对页表本身采用分页式管理,对页表本身增加了一层页表管理。(其实这种管理方式是很通用的,后面学到的文件系统,inode结点也可以像这样存储)
  3. 多级页表,多级页表和二级页表是为了节省物理内存空间。使得页表可以在内存中离散存储。(单级页表为了随机访问必须连续存储,如果虚拟内存空间很大,就需要很多页表项,就需要很大的连续内存空间,但是多级页表不需要。)
  4. 缺页中断,当软件试图访问已映射在虚拟地址空间中,但是目前并未被加载在物理内存中的一个分页时,由中央处理器的内存管理单元所发出的中断。 参考博文
  1. 上图是硬性的,首先,虚拟内存转换成对应的页号,查找页表,发现该页的中断位为0,表示该页是缺页的,中断处理程序查存储分块表,找空闲块;根据页在辅存的位置,启动磁盘读信息,把从磁盘上读出的信息装入到分配的内存块中,修改页表中相应的表目内容,修改存储分块表里相应表目的状态,在完成所需页面的装入后返回原指令重新执行。当硬性页缺失过于频繁的发生时,称发生系统颠簸。
  2. 软性的,就是相关的页已经被加载进内存,但是没有向MMU注册的情况
  3. 当程序访问的虚拟地址是不存在于虚拟地址空间内的时候,则发生无效页缺失。一般来说这是个软件问题,但是也不排除硬件可能,比如因为内存故障而损坏了一个正确的指针。
  1. 页面置换算法,进程运行过程中,如果发生缺页中断,而此时内存中有没有空闲的物理块是,为了能够把所缺的页面装入内存,系统必须从内存中选择一页调出到磁盘的对换区。
  1. 最佳置换(OPT),淘汰将来不再使用或者在最远的将来才可能被使用的页。但是该算法需要依据以后各业的使用情况,而当一个进程还未运行完成,很难估计哪一个页面是以后不再使用或在最长时间以后才会用到的页面。所以该算法是不能实现的。它可作为衡量其它算法优劣的一个标准
    举个例子:
    访问到3的时候,比较内存里面的0,2,3哪一个最近不会被使用,看了后面的访问顺序,发现是5不再使用了,所以就淘汰5,以此类推
  2. 先进先出淘汰算法(FIFO),淘汰最先调入内存的页
  3. 最近最少使用淘汰算法(LRU),淘汰最近最久没有被使用的页。LRU算法普偏地适用于各种类型的程序,但是系统要时时刻刻对各页的访问历史情况加以记录和更新,开销太大,因此LRU算法必须要有硬件的支持。系统使用特殊的堆栈来存放内存中每一个页面的页号。每当访问一页时就调整一次,即把被访问页面的页号从栈中移出再压入栈顶。因此,栈顶始终是最新被访问页面的页号栈底始终是最近最久未被访问的页号。当发生缺页中断时,总是淘汰栈底页号所对应的页面
  4. **抖动现象:**刚被淘汰的页,又要被访问,频繁发生。
  5. 工作集:是指进程在某个时间段里要访问的页的集合。思路:页置换的时候,锁住缺页的进程,不让其缓冲。而调入的页占据那些暂时得不到执行的进程所占据的内存区域。
段式存储管理