Linux 内存 —— 内存分页、分段

#Linux #memory #Operation #BasicKnowledge

内存分页与内存分段操作系统管理虚拟内存的方式。

分段:将进程内存分为代码分段数据分段栈段堆段等逻辑分明的段。能产生连续的物理空间。容易造成内存碎片,内存交换率低。 分页:将物理内存与虚拟内存都分为一个个固定大小的页,内存以页为单位分配、回收、释放。适配局部性原理,碎片利用率高,可以不加载整个内存。

kernel_memory_management/✍ 文章/一文带你了解,虚拟内存、内存分页、分段、段页式内存管理.md at main · 0voice/kernel_memory_management

前置知识:[[Linux 内存 —— 虚拟内存]]

内存分段

内存分段是操作管理虚拟地址与物理地址之间关系的方式之一。

[!important] 程序是由若干个逻辑分段组成的,可由代码分段数据分段栈段堆段组成。不同的段是有不同的属性的,所以就用分段(Segmentation)的形式把这些段分离出来。

如何映射

分段机制下的虚拟地址由两部分组成,段内选择子段内偏移量

  • 段选择子里面最重要的是段号,用作段表的索引。段表里面保存的是这个段的基地址、段的界限和特权等级等。
  • 段内偏移量应该位于 0 和段界限之间,如果段内偏移量是合法的,就将段基地址加上段内偏移量得到物理内存地址。
Pasted image 20231110131944.png

有一些不足之处:

  • 第一个就是内存碎片的问题。
  • 第二个就是内存交换的效率低的问题。

内存碎片

内存碎片的问题共有两处地方:

  • 外部内存碎片,也就是产生了多个不连续的小物理内存,导致新的程序无法被装载;
  • 内部内存碎片,程序所有的内存都被装载到了物理内存,但是这个程序有部分的内存可能并不是很常使用,这也会导致内存的浪费;

解决外部内存碎片的问题就是内存交换

Pasted image 20231110132032.png

步骤:

  • 将程序内存写到磁盘上
  • 从磁盘上读回内存,并整合内存碎片

[!info] 可以把音乐程序占用的那 256MB 内存写到硬盘上,然后再从硬盘上读回来到内存里。不过再读回的时候,我们不能装载回原来的位置,而是紧紧跟着那已经被占用了的 512MB 内存后面。这样就能空缺出连续的时间 256MB 空间,于是新的 200MB 程序就可以装载进来。

这个内存交换空间,在 Linux 系统里,也就是我们经常看到的 Swap 空间,这块空间是从硬盘划分出来的,用于内存与硬盘的空间交换

内存分页

分段的好处就是能产生连续的内存空间,但是会出现内存碎片和内存交换的空间太大的问题。

要解决这些问题,那么就要想出能少出现一些内存碎片的办法。另外,当需要进行内存交换的时候,让需要交换的写入或者从磁盘装载的数据更少一点,这样就可以解决问题了。这个办法,也就是内存分页Paging)。

页表实际上存储在 CPU 的内存管理单元 (MMU) 中,于是 CPU 就可以直接通过 MMU,找出要实际要访问的物理内存地址。

[!important] 分页是把整个虚拟和物理内存空间切成一段段固定大小的Page)。一个页是一个连续并且尺寸固定的内存空间。在 Linux 下,每一页的大小为 4KB

虚拟地址与物理地址之间通过页表来映射,如下图:

Pasted image 20231110132824.png

如何解决内存碎片、内存交换效率低

由于内存空间都是预先划分好的,也就不会像分段会产生间隙非常小的内存,这正是分段会产生内存碎片的原因。而采用了分页,那么释放的内存都是以页为单位释放的,也就不会产生无法给进程使用的小内存。

如果内存空间不够,操作系统会把其他正在运行的进程中的「最近没被使用」的内存页面给释放掉,也就是暂时写在硬盘上,称为换出Swap Out)。一旦需要的时候,再加载进来,称为换入Swap In)。所以,一次性写入磁盘的也只有少数的一个页或者几个页,不会花太多时间,内存交换的效率就相对比较高。

更进一步地,分页的方式使得我们在加载程序的时候,不再需要一次性都把程序加载到物理内存中。我们完全可以在进行虚拟内存和物理内存的页之间的映射之后,并不是真的把页加载到物理内存里,而是只有在程序运行中,需要用到对应虚拟内存页里面的指令和数据时,再加载到物理内存里面去。

如何映射

在分页机制下,虚拟地址分为两部分,页号页内偏移。页号作为页表的索引,页表包含物理页每页所在物理内存的基地址,这个基地址与页内偏移的组合就形成了物理内存地址,见下图。

Pasted image 20231110133623.png

三个步骤:

  • 把虚拟内存地址,切分成页号和偏移量;
  • 根据页号,从页表里面,查询对应的物理页号;
  • 直接拿物理页号,加上前面的偏移量,就得到了物理内存地址。

局部性与 Cache

在一段时间内,整个程序的执行仅限于程序中的某一部分。相应地,执行所访问的存储空间也局限于某个内存区域。

可把最常访问的几个页表项存储到访问速度更快的硬件,于是加入了一个专门存放程序最常访问的页表项的 Cache,这个 Cache 就是 TLB(Translation Lookaside Buffer) ,通常称为页表缓存、转址旁路缓存、快表等。

段页式内存管理

段页式内存管理实现的方式:

  • 先将程序划分为多个有逻辑意义的段,也就是前面提到的分段机制;
  • 接着再把每个段划分为多个页,也就是对分段划分出来的连续空间,再划分固定大小的页;
Pasted image 20231110134320.png

这样,地址结构就由段号、段内页号和页内位移三部分组成。

段页式地址变换中要得到物理地址须经过三次内存访问:

  • 第一次访问段表,得到页表起始地址;
  • 第二次访问页表,得到物理页号;
  • 第三次将物理页号与页内位移组合,得到物理地址。

可用软、硬件相结合的方法实现段页式地址变换,这样虽然增加了硬件成本和系统开销,但提高了内存的利用率。