内存管理1

理解系统中的内存管理

Posted by BY beta on February 23, 2021

前言

内存管理 是指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如何高效、快速的分配,并且在适当的时候释放和回收内存资源。

为什么需要内存管理?

  1. 不同的进程应该相互独立
  2. 内存的使用效率要高

虚拟内存管理的优点和缺点?

  1. 多进程共享物理内存
  2. 进程相互独立
  3. 每个进程可以使用大规模/连续的内存
  4. 更高效的内存使用
  5. 缺点:虚拟内存到物理内存需要额外的设备,昂贵并且速度变慢

直接使用物理内存管理

基本概念

  1. 物理地址和虚拟地址

    物理地址是真实的与CPU对应的地址;虚拟地址(线性地址)

1.进程在内存中的布局

程序编译成二进制代码:

image-20210224161824498

进程在内存中的布局,代码段、数据段分开

image-20210224161944745

  1. cpu直接操作物理内存。进程根据代码段提供的位置,操作内存中该位置的数据。这应该是实模式

image-20210224162320485

缺陷:

  • 编译器需要非常小心,因为多个进程之间的内存需要相互隔离
  1. CPU操作虚拟地址,需要MMU转换。进程在虚拟内存的布局如右上,实际的内存布局如右下。

image-20210224163826341

2.虚拟内存与物理内存的对应

对于每一个进程,理论上都有4G的内存可以使用,但在实际的内存布局中,并不是占用4G的物理内存。

多个进程在物理内存的布局如下

image-20210224164753439

3.分段和分页

分段内存管理

分段将物理内存分成不同的段,其优点在于:1. 将内存分为不同的段,利于进程独立;2.不同的段给与不同的权限,比如某些数据段“write only” 实现了对内存的保护。

分段表给出了段地址和权限

image-20210224165207915

分页内存管理

分页表存储在内存中

image-20210224165801821

因为分页表很大,查询的时间较长,为了提高查表的效率,会将一部分页表存储在cache中,成为TLB技术。

TLB分页技术如下,对于地址先查询cache。由于计算机中的局部效应,效率会有很大的提升。

TLB有不同设计:

  • 每个进程都有自己的TLB。
  • TLB只有一个,给不同的进程标记PID

image-20210224170210742

分段分页同时(x86)

出现三种地址:逻辑地址、线性地址和物理地址

image-20210224170526008

4. Cache

cache是CPU为了提高速度的缓存机制。实际cache实现存在两种不同的机制

1.先做cache,再做MMU

image-20210224172048442

2.先做MMU,再做cache

image-20210224172129896

对比如下:

image-20210224172232816

物理cache和逻辑cache对比

  1. 物理cache速度慢,但是不需要清空
  2. 逻辑cache速度快,但是在context switch时会清空,或者存储Pid

5.物理内存管理

​ 在kernel所在的内存中,存在一个memory info table,如下

image-20210224204841436

内核提供一些接口,提供内存分配、释放的功能

物理内存管理必须存在,因为操作系统启动之后,就需要内存的分配。

如果没有MMU,如何保护内存访问?

  • 利用memory protection uints

内存管理面临的挑战:

  • 内部碎片,分配的内存大于所需的内存,有一部分浪费
  • 外部碎片,系统有足够的内存提供,但是因为内存不连续而无法提供

6.外部碎片

进程多次分配和释放之后,导致内存不连续,出现许多碎片

image-20210224214002811

直观而言,对内存采取移动,可以实现最后空闲内存是连续的。但是进程进行内存移动,存在实际的困难:

  • 其他进程都需要被锁住
  • 导致进程内部的地址都需要改变
  • 时间花费很长。

解决方法:

frame table:每个frame的内存大小保持固定。

7. kernel memory management

需要解决的问题:

  • 线性时间的内存分配和回收
  • 外部碎片
  • 内部碎片
  • 同时满足大内存和小内存的分配

8. x86 内核内存管理为例

buddy system 解决了内存线性时间分配的问题,但是存在很严重的内部碎片问题。linux为了解决这一问题,设置了slab allocator 。

image-20210225111406619

buddy system

把所有的空闲页框分组为11个块链表,每个块链表分别包含大小为1,2,4,8,16,32,64,128,256,512和1024个连续页框的页框块。最大可以申请1024个连续页框,对应4MB大小的连续内存。每个页框块的第一个页框的物理地址是该块大小的整数倍

image-20210225111812154

slab allocator

slab主要针对一些操作频繁,且内存较小的对象,比如进程描述符等。

slab是Linux操作系统的一种内存分配机制。其工作是针对一些经常分配并释放的对象,如进程描述符等,这些对象的大小一般比较小,如果直接采用伙伴系统来进行分配和释放,不仅会造成大量的内碎片,而且处理速度也太慢。而slab分配器是基于对象进行管理的,相同类型的对象归为一类(如进程描述符就是一类),每当要申请这样一个对象,slab分配器就从一个slab列表中分配一个这样大小的单元出去,而当要释放时,将其重新保存在该列表中,而不是直接返回给伙伴系统,从而避免这些内碎片。slab分配器并不丢弃已分配的对象,而是释放并把它们保存在内存中。当以后又要请求新的对象时,就可以从内存直接获取而不用重复初始化。

image-20210225112048813