操作系统-内存管理
本文最后更新于:1 年前
[TOC]
分段管理
进程的地址空间:按照程序自身的逻辑关系划分(代码段,数据段……)为若干个段,每个段都有一个段名(在低级语言中,程序员使用段名来编程),每段从 0 开始编址。
内存分配规则:以段为单位进行分配,每个段在内存中占据连续空间,但各段之间可以不相邻。
由于是按逻辑功能模块划分,用户编程更方便,程序可读性更高。
逻辑地址:段号:段内地址
段表:段号=段基地址=段长。
缺点:
- 内存碎片。
- 内存交换效率低。
分页管理
分页式
固定分区会产生碎片。把内存划分为很多块,作为进程内存分配的最小单位。
页面大小要权衡,应该是 2 的幂次。太大会产生页内碎片,太小,页表会占用大量内存,降低换入换出效率。
分页管理的地址结构:页号:页内偏移量为 32 位,0-11 为页内偏移,12-31 为页号。
每页大小 2^12=4kb。
为了方便每个进程在内存中找到每个页面对应的物理块,会为每个进程创建一张页表(存储在内存中===访问一次数据需要访问两次内存)。
页表由页表项组成第一部分是,页号,第二部分是物理块号。
分页管理问题
- 每次访问内存都需要进行地址的转换。速度受限制。
- 页表不能太大,否则内存利用率会降低。
改进
具有并行查找能力的高速缓冲存储器==快表(联想存储器 TLB)–把最常访问的⼏个⻚表项存储到访问速度更快的硬件。
这时先查快表,没有再查慢表。(局部性原理)
多级页表
虚拟内存地址空间 4Gb,页表大小是 4kb,需要 100 万个页,假设每个页表项大小为 4b。那么需要 4Mb 来存储页表。
每个进程都有自己的页表,假设有 200 个进程,就需要 800Mb 来存储页表,很大了。
建立上一级页表存储页表间的映射关系。
顶级页表一般只有一个页面。
建立多级页表:建立索引,不用去存储无用页表项,也不用去盲目的查找页表项。
段页式管理
- 分段利于反应程序的逻辑结构以及段的共享。
- 先将程序划分为多个有逻辑意义的段,接着再把每个段划分为多个⻚,也就是对分段划分出来的连续空间,再划分固定⼤⼩的⻚;
- 地址结构就由段号、段内⻚号和⻚内位移三部分组成。
- ⽤于段⻚式地址变换的数据结构是每⼀个程序⼀张段表,每个段⼜建⽴⼀张⻚表,段表中的地址是⻚表的起始地址,⽽⻚表中的地址则为某⻚的物理⻚号。
- 第⼀次访问段表,得到⻚表起始地址;
- 第⼆次访问⻚表,得到物理⻚号;
- 第三次将物理⻚号与⻚内位移组合,得到物理地址。
虚拟内存
基于局部性原理:程序装入时,先装入一部分,其他的驻留在外存,当访问的信息不在内存时,再换入。
这好像给用户提供了一个比实际内存大的内存空间。
使用外存的空间来扩展内存的空间。
实现:
分页
分段
段页式
需要:
内外存
页表机制
中断机构(缺页中断)
地址变换(先检索快表)
总结
为了在多进程环境下,使得进程之间的内存地址不受影响,相互隔离,于是操作系统就为每个进程独⽴分
配⼀套虚拟地址空间,每个程序只关⼼⾃⼰的虚拟地址就可以,实际上⼤家的虚拟地址都是⼀样的,但分
布到物理地址内存是不⼀样的。作为程序,也不⽤关⼼物理地址的事情。
每个进程都有⾃⼰的虚拟空间,⽽物理内存只有⼀个,所以当启⽤了⼤量的进程,物理内存必然会很紧
张,于是操作系统会通过内存交换技术,把不常使⽤的内存暂时存放到硬盘(换出),在需要的时候再装
载回物理内存(换⼊)。
那既然有了虚拟地址空间,那必然要把虚拟地址「映射」到物理地址,这个事情通常由操作系统来维护。
那么对于虚拟地址与物理地址的映射关系,可以有分段和分⻚的⽅式,同时两者结合都是可以的。
内存分段是根据程序的逻辑⻆度,分成了栈段、堆段、数据段、代码段等,这样可以分离出不同属性的
段,同时是⼀块连续的空间。但是每个段的⼤⼩都不是统⼀的,这就会导致内存碎⽚和内存交换效率低的
问题。
于是,就出现了内存分⻚,把虚拟空间和物理空间分成⼤⼩固定的⻚,如在 Linux 系统中,每⼀⻚的⼤⼩
为 4KB 。由于分了⻚后,就不会产⽣细⼩的内存碎⽚。同时在内存交换的时候,写⼊硬盘也就⼀个⻚或
⼏个⻚,这就⼤⼤提⾼了内存交换的效率。
再来,为了解决简单分⻚产⽣的⻚表过⼤的问题,就有了多级⻚表,它解决了空间上的问题,但这就会导
致 CPU 在寻址的过程中,需要有很多层表参与,加⼤了时间上的开销。于是根据程序的局部性原理,在
CPU 芯⽚中加⼊了 TLB,负责缓存最近常被访问的⻚表项,⼤⼤提⾼了地址的转换速度。
Linux 系统主要采⽤了分⻚管理,但是由于 Intel 处理器的发展史,Linux 系统⽆法避免分段管理。于是
Linux 就把所有段的基地址设为 0 ,也就意味着所有程序的地址空间都是线性地址空间(虚拟地址),相
当于屏蔽了 CPU 逻辑地址的概念,所以段只被⽤于访问控制和内存保护。
另外,Linxu 系统中虚拟空间分布可分为⽤户态和内核态两部分,其中⽤户态的分布:代码段、全局变量、
BSS、函数栈、堆内存、映射区