3.1 内存

1.内存的基础知识

image-20260501144359649

内存有什么用?

程序从硬盘调入内存,被CPU处理。

如何区分内存中的多个程序放置的位置呢? →需要对内存进行编址。

image-20260501144830716

从写程序 到 程序运行

编写源代码 –→

编译(高级语言(高级代码)到机器语言(机器指令)–→

链接(把code连接到一起,且形成逻辑地址)–→

装入(装入内存,形成物理地址)

image-20260501151425275

链接的三种方式

  • 静态链接:程序运行前一次性链接,之后不拆开。
  • 装入时动态链接:边装入内存边链接。
  • 运行时动态链接:程序运行时才链接。

image-20260501151603712

机器指令的工作原理

结构:操作码+若干参数(可能包含地址参数)

指令告诉CPU从哪个地址读/写数据。

或者告诉CPU对数据进行什么处理。

image-20260501145739956

物理地址 VS 逻辑地址

程序经过编译、链接后生成的指令中指明的地址是逻辑地址。

image-20260501150249519

如何实现(代码中的)逻辑地址转换到(内存中的)物理地址?

  • 绝对装入

  • 可重定位装入(静态重定位)

  • 动态运行时装入(动态重定位)

​ 绝对装入:只适用于单道程序环境。(灵活性低,不可迁移)

image-20260501150502034

​ 静态重定位:

​ 地址变换在 装入时 一次完成的。

​ 进程装入内存时,分配进程要求的全部地址空间。

​ 进程一旦进入内存,就不可以移动位置了。

image-20260501150804513

​ 动态重定位:

​ 有一个重定位寄存器,地址转换在程序真正要执行的时候进行。

​ 只装入部分代码,根据需要动态分配内存。

​ 允许程序在内存中发生移动。

image-20260501151113242

回顾

image-20260501152147052

2.内存管理的概念⭐

os负责:

  • ①内存空间的分配与回收
  • ②内存空间的扩充
  • ③地址转换
  • ④存储保护

①OS负责:对内存空间的分配与回收(连续分配和非连续分配方式)

②OS负责:对内存空间的扩展(覆盖,交换和虚拟内存技术)

③OS负责:提供地址转换功能,程序的逻辑地址与物理地址之间的转换。(三种装入方式)

image-20260502132824138

④OS负责:提供内存保护功能。(保证各自进程在自己的空间运行,不会越界访问)

image-20260502133613743

image-20260502133759898

回顾

image-20260502133821910

3.进程的内存映像

image-20260502134934959

image-20260502135032516


内存扩展 – 覆盖与交换

  • 虚拟存储技术(之后学到)

  • 覆盖技术

  • 交换技术

覆盖技术

把程序分成多个段。

把内存分为一个固定区和多个覆盖区

常用的段放在固定区,调入之后不再调出。

不常用的段放在覆盖区,需要时调入内存,不需要时调出内存。

必须由程序员实现,对用户不透明,增加编程负担。

image-20260502143521384

交换技术

进程在内存和磁盘之间动态调度。

when内存空间紧张时,系统将内存中的某些进程暂时调出外存,把外存中已具备条件的进程换入内存。

  • Note:中级调度,就是决定把哪一个处于挂起状态的进程重新调入内存。

image-20260502144554224

回顾

覆盖,是在同一个进程或程序中进行的。

交换,是在不同进程(或作业)之间的。

image-20260502144751584

①内存空间的分配与回收 – 连续分配管理方式

连续分配:系统给进程分配的必须是一个连续的内存空间

  • 单一连续分配
  • 固定分区分配
  • 动态分区分配

单一连续分配

内存被分为系统区和用户区。

内存中只能有一道用户程序,即用户程序独占整个用户区。

image-20260502145739338

固定分区分配

内存分为多个分区。(分区大小可以相等,可以不等)

每一个分区只能装入一道程序。

OS需要建立一个分区说明表。

image-20260502150040465

动态分区分配

不会预先划分内存区,(在进程装入内存时),根据进程大小建立合适的分区。

没有内部碎片,有外部碎片。

外部碎片:内存中某些内存分区太小难以利用。

解决外部碎片:紧凑技术。(挪移拼接)

image-20260502150645753

1.建立空闲分区表或者空闲分区链

image-20260502150807572

2.用动态分区分配算法(之后介绍)

3.两个相邻的空闲分区合并。

image-20260502151042749

回顾

image-20260502152038719

动态分区分配算法

image-20260502153630363

首次适应:按低地址开始寻找(在空闲分区表/分区链找到第一个大小满足的空闲分区)

邻近适应:每次分配内存从上次结束位置开始查找空闲分区表/链。(空闲分区以地址递增的顺序排列(可以是循环链表)

最佳适应:优先使用更小的空闲分区。(把空闲分区按大小递增链接)

  • 缺点:会留下很多很小的外部碎片。(eg10给了9)

最坏适应:优先使用最大的空闲区。

  • 缺点:之后有大进程来的话,没有空闲分区可用。

image-20260502155521919

回顾

image-20260502155633696

①内存空间的分配与回收 – 非连续分配管理方式

非连续分配:为用户进程分配的可以是分散的内存空间。

把进程分页或者分段,离散的放进内存中。

  • 基本分页存储管理
  • 基本分段存储管理
  • 段页式存储管理

分页存储

内存分成一个一个内存块(又叫页框

进程分成一个一个页/页面(页大小=块大小)

image-20260502175231190

页表

记录页号和块号的映射关系。

一个进程对应一张页表。

进程每一页对应一个页表项。

一个页表项由“页号”和“块号”组成。

image-20260502175501759

Q1:每个页表项占多少字节?

Note:页号是隐藏的,不记位数。只看块号的位数就行。

​ (页面偏移量→页面大小)

image-20260502175909170

Q2:如何实现地址转换(逻辑地址,物理地址)?

逻辑地址A对应的物理地址 = P号页面在内存中的起始地址 + 页内偏移量W

即物理地址 = 对应页号初始地址表示 + 偏移量表示。

image-20260502180136737

image-20260502181002365

页面大小为2的k次方有什么好处?

无需进行除法运算,就能得到逻辑地址对应的页号和页内偏移量。(二进制数末尾k位就是页内偏移量,其余部分就是页号)

除法运算:

​ 页号 = 逻辑地址/页面大小

​ 偏移量 = 逻辑地址%页面大小

image-20260502181025761

image-20260502181138545

回顾

image-20260502181323797

基本地址变换机构

就是:用于实现逻辑地址到物理地址转换的一组硬件结构。

页表寄存器

  • 进程未被执行时,页表在内存的始址和页表长度放在PCB中。

  • 当进程被调度时,os内核会把它们放到页表寄存器中。

image-20260503133700782

地址变换过程

image-20260503133239814

页式管理中的地址是一维的

即只要给出一个逻辑地址,系统就可以自动算出页号和页内偏移量,并不需要告诉系统页内偏移量占多少位。(因为可由页面大小得知)

image-20260503134228783

进程页表通常是装在连续的内存块中的。(也成全了下条)

为了方便页表的查询,常常会让一个页表项占用更多的字节,使得每个页面恰好可以装得下整数个页表项。

image-20260503135328051

回顾

两次访存:一次是查询页表,一次是访问目标内存单元。

image-20260503135744197

具有快表的地址变换机构

image-20260503135859582

快表TLB

  • 是一种高速缓存Cache!不是内存。
  • 存放最近访问的页表项的副本。(普通cache会有各种数据的副本,快表cache就很单一了哈哈哈)
  • 与之对应,页表称为慢表。

如果快表命中,只需要一次访存

如果快表没命中,需要两次访存。(访存费时间,访问cache快很多)

image-20260503144019013

局部性原理

image-20260503145149322

基于局部性原理,快表命中率可以达到90%以上。image-20260503144857311

回顾

image-20260503145306205

两级页表

image-20260503150732663

单级页表存在的问题

问题一:页表必须连续存放,如果页表很大,需要占用很多个连续的页框,和内存离散分配的初衷相反。

问题二:没有必要让整个页表常驻内存,进程在一段时间内可能只需要访问某几个特定的页面。

image-20260503151235231

问题一solute:把页表分块,用页目录表(外层页表)索引。

image-20260503151621051

image-20260503151832655

image-20260503152544714

问题二solute:虚拟存储技术

image-20260503152524167

Note:

如果采用多级页表机制,则各级页表大小不能超过一个页面,可以采用更多级别的页表。

两级页表新的问题:三次访存(假设没有快表)

image-20260503152835939

回顾

分段存储

image-20260503190307523

分段

进程按照自身逻辑分为多个段,每个段长度不等。

每一个段都有一个段名(编译程序会转为段号)。每一个段编址都是从0开始。

各段之间可以不相邻。

image-20260503190638715

逻辑地址就是:段号 | 段内地址

image-20260503190855433

段表

段表记录每一个段在内存中存放的位置。

构成:段号,段长,段基址。

各个段表项的长度是相同的?怎么确定?

image-20260503191237671

地址变换

image-20260503192008262

分段分页对比

分页:

  • 实现离散分配,提高内存利用率。
  • 对用户是不可见的
  • 页的大小固定,由系统决定。
  • 分页的用户进程,地址空间是一维的。程序员只需给出一个逻辑地址即可表示地址。

分段:

  • 更好满足用户需求。
  • 对用户是可见的
  • 段的大小不固定,用户编写的程序决定。
  • 分段的用户进程,地址空间是二维的。程序员标识地址时,既要给出段名,也要给出段内地址。
  • 分段更容易实现信息的共享和保护。
  • 同样可以引入“快表”

image-20260503192350564

回顾

image-20260503193210346

段页式存储

image-20260503193247270

分页和分段的优缺点

image-20260503193543943

段页式

先按模块分段,再将各段分页。

image-20260503193951175

逻辑地址

image-20260503193813729

段表,页表

一个进程对应一个段表,可能会对应多个页表。

image-20260503194247319

逻辑地址转为物理地址

三次访存(无快表)

image-20260503194626978

回顾

image-20260503194705897