【4. 操作系统—非连续内存分配】

简介: 第三章介绍的是`连续内存管理`, 即 : 操作系统加载到内存以及程序加载到内存中时, 需要为其分配一块连续的空闲(内存)块. 但是容易出现碎片问题, 这一章介绍的非连续内存分配可以有效的减少碎片的出现.

❤️操作系统的非连续内存分配

🎾1. 概述

第三章介绍的是 连续内存管理, 即 : 操作系统加载到内存以及程序加载到内存中时, 需要为其分配一块连续的空闲(内存)块. 但是容易出现碎片问题, 这一章介绍的非连续内存分配可以有效的减少碎片的出现.

为了避免产生过多碎片,而使用非连续内存分配策略

  • 分段(Segmentation)
  • 分页(Paging)
  • 页表(Page Table)

    • 页表概述
    • 快表(TLB)
    • 二级/多级页表
    • 反向页表

连续内存分配的缺点

  • 分配给一个程序的物理内存是连续的
  • 内存利用率低
  • 有外碎片 / 内碎片的问题

非连续内存分配的优点

  • 一个程序的物理地址空间是非连续的
  • 更好的内存利用和管理
  • 允许共享代码与数据(共享库等...)
  • 支持动态加载和动态链接

非连续内存分配的缺点

  • 建立虚拟地址和物理地址的转换难度大

    • 软件方案(开销太大,不选择)
    • 硬件方案(采用硬件方案) : 分段 / 分页

🎾2. 分段

需要解决的问题

  1. 程序的分段地址空间的寻址
  2. 分段寻址方案
  • : 在程序中会有来自不同文件的函数 ; 在程序执行时, 不同的数据也有不同的字段, 比如 : 堆 / 栈 / .bss / .data 等
  • 分段:更好的分离和共享

1661138035138.png

程序的分段地址空间如下图所示 :
1661138071042.png

1661138088481.png

🎱2.1 分段寻址方案

  • 软件可以访问,但是对于每一次内存访问,软件每次都需要进行一次映射,导致内存开销很大,所以使用硬件访问

程序是一个连续的一维的逻辑地址空间,但是物理地址空间不连续,使用映射机制进行关联.

俩种实现机制

  1. 段寄存器+地址寄存器(段寄存器和地址寄存器是分开的)
  2. 单地址实现(段寄存器和地址寄存器不分开)

1661138107686.png

🎱2.3 硬件实现方案

  • 根据段号找到,物理地址的起始地址
  • 根据偏移找到,整个长度限制
  • 操作系统建立段表,它维护这个段表(段号,物理地址的起始地址,长度限制)
  • 物理地址 : 段表中的起始地址 + 二元组中的偏移地址

1661138126617.png

  • 当查到相应的物理地址,CPU会将查到的段的限制和实际物理地址对比,当超出限制,CPU会产生一个中断异常,交给OS处理。如果满足限制,则段号+偏移产生物理地址,CPU根据物理地址将相应的数据取出来

🎾3. 分页

  • 分页

    • 分页地址空间
    • 页寻址方案

🎱3.1 分页地址空间

  • 段和页区别:在分段中段的尺寸是可以变化的,在页中,页或者页帧的大小是固定不变的
  • 完成物理页表和逻辑页表的映射

1661138144709.png

页帧(Frame)(物理地址)

1661138270087.png

分页和分段的最大区别 : 这里的 S 是一个固定的数, 而分段中的长度限制不定

例子

1661138299022.png

页(Page)(逻辑地址)

1661138314442.png

🎱3.2 页寻址机制

操作系统维护一张页表, 页表保存了逻辑地址——物理地址之间的映射关系

存储 : (页号, 帧号)

  • 逻辑地址空间应当大于物理内存空间
  • 页映射到帧
  • 页是连续的虚拟内存
  • 帧是非连续的物理内存(有助于减少碎片的产生)
  • 不是所有的页都有对应的帧

分页机制和分段不一样,分页机制页内的偏移大小是一样的
1661138333219.png

逻辑地址是连续的映射到物理空间后是不连续的,这有利于减少内存碎片

1661138345915.png

🎾4. 页表

🎱4.1 页表概述

  • 页表概述
  • 转换后备缓冲区(TLB)
  • 二级/多级页表
  • 反向页表

🎱4.2 页表结构

  • CPU根据程序的page的页号的若干位, 计算出索引值index, 在页表中搜索这个index, 得到的是帧号, 帧号和原本的offset组成物理地址.

1661138361508.png

地址转换实例

1661138382030.png

分页机制的性能问题

  • 访问一个内存单元需要2次内存访问

    • 一次用于获取页表项
    • 一次用于访问数据
  • 页表可能非常大

    • 64位机器如果每页1024字节, 那么一个页表的大小会是多少?(2^64 / 2^10 = 2^54 存放不下)

每一个运行的程序都需要有一个页表

如何处理?

  • 缓存(Caching):将经常用到的数据和内容缓存到离CPU很近的地方——————>速度上
  • 间接(Indirection)访问:把很大的空间,拆分成比较小的空间(多级页表)————>空间上

🎱4.3 TLB缓冲(速度上)

  • MMU:CPU中的内存管理单元的TLB缓冲,缓冲页表的内容

当CPU得到逻辑地址后,根据key查找TLB表,找到f,+offset直接获得物理地址,避免了对一次页表的访问,当TLB访问不到,CPU会查找页表,如果页表的标志位为1,则存在,于是把页表中的Frame num取出来,放到TLB中
1661138398903.png

🎱4.5 二级页表、多级页表(空间上)

二级页表

  • 将页号分为两个部分, 页表分为两个, 一级页号对应一级页表, 二级页号对应二级页表.
  • 一级页号查表获得在二级页表的起始地址, 地址加上二级页号的值, 在二级页表中获得帧号

步骤

  1. page num 分成俩块P1和P2,对应一级页表页号和二级页表页号,offset没变。从而使得对大地址寻址变成对n个小地址寻址
  2. 首先找一级页表,一级页表起始地址CPU知道,将P1的起始地址作为index,查找一级页表的页表项,存的是二级页表的起始地址。
  3. 把P2作为二级页表的index+一级页表中存储的二级页表起始地址,形成针对P2的页表项,它里面存的是Frame num,在加offset获得物理地址

时间交换空间

  • 节约了一定的空间, 在一级页表中如果resident bit = 0, 可以使得在二级页表中不存储相关index,而只有一张页表的话, 即使映射关系不存在,对应的一些index都需要保留

1661138421402.png

多级页表

  • 通过把页号分为k个部分, 来实现多级间接页表, 建立一棵页表"树"

1661138436545.png

🎱4.6 反向页表

  • 逻辑空间越大,寻址空间越大,对应的页表就越多,使得页表项不与逻辑空间产生直接关系,与物理地址建立映射关系

解决大地址空间问题

  • 目的 : 根据物理页帧号获得逻辑页号(与之前方法相反)
  • 反向页表只需要存在一张即可

1661138449729.png

⛳️4.6.1 基于页寄存器的方案

  • 以页帧号为索引(物理地址),以页号为页表项的内容(逻辑地址),之前的页表都是以页号为索引,页帧号为内容,这种反向存储使得计算机容量只有物理地址有关系
  • 存储 (帧号, 页号) 使得表大小与物理内存大小相关, 而与逻辑内存关联减小.

问题

  • 我们需要根据Page num查找Frame num,而这个表是以Frame num 为索引的

1661138463482.png

1661138484315.png

优势 :

  • 转换表的大小相对于物理内存来说很小
  • 转换表的大小跟逻辑地址空间的大小无关

劣势 :

  • 需要的信息对调了, 即根据帧号可以找到页号
  • 如何转换回来? (如何根据页号找到帧号)
  • 在需要在反向页表中搜索想要的页号

⛳️4.6.2 基于关联内存的方案

  • 并行的查找页号所对应的页帧号
  • 硬件设计复杂, 容量不大, 需要放置在CPU中

1661138498347.png

  • 如果帧数较少, 页寄存器可以被放置在关联内存中
  • 在关联内存中查找逻辑页号

    • 成功 : 帧号被提取
    • 失败 : 页错误异常 (page fault)
  • 限制因素:

    • 大量的关联内存非常昂贵(难以在单个时钟周期内完成 ; 耗电)

⛳️4.6.3 基于哈希查找的方案

  • 哈希函数 : h(PID, p) 从 PID 标号获得页号

1661138512426.png

在反向页表中通过哈希算法来搜索一个页对应的帧号

  • 对页号做哈希计算, 为了在帧表中获取对应的帧号
  • 页 i 被放置在表 f(i) 位置, 其中 f 是设定的哈希函数
  • 为了查找页 i , 执行下列操作 :

    • 计算哈希函数 f(i) 并且使用它作为页寄存器表的索引, 获取对应的页寄存器
    • 检查寄存器标签是否包含 i, 如果包含, 则代表成功, 否则失败
目录
相关文章
|
1月前
|
存储 Linux 调度
深入理解操作系统:从进程管理到内存分配
【8月更文挑战第44天】本文将带你深入操作系统的核心,探索其背后的原理和机制。我们将从进程管理开始,理解如何创建、调度和管理进程。然后,我们将探讨内存分配,了解操作系统如何管理计算机的内存资源。最后,我们将通过一些代码示例,展示这些概念是如何在实际操作系统中实现的。无论你是初学者还是有经验的开发者,这篇文章都将为你提供新的视角和深入的理解。
|
2月前
|
安全 索引
操作系统中的内存管理策略
【8月更文挑战第23天】
70 1
|
6天前
|
分布式计算 算法 大数据
探索操作系统的核心:调度与内存管理机制
【10月更文挑战第11天】 本文深入探讨了操作系统中两大核心功能——调度与内存管理机制。通过分析调度算法、进程状态转换及内存分配策略等关键方面,揭示了它们如何共同维护系统性能和稳定性。旨在为读者提供对操作系统内部运作的深刻理解,同时引起对优化策略的思考。
22 5
|
15天前
|
算法
深入理解操作系统:内存管理机制的探索之旅
【10月更文挑战第2天】在数字世界的浩瀚海洋中,操作系统犹如一艘精密的航船,承载着软件与硬件的和谐共舞。本文将揭开内存管理的神秘面纱,从基础概念到高级策略,引领读者领略操作系统内存分配的智慧。通过深入浅出的解释和生动的比喻,我们一同遨游在内存的江河之中,感受操作系统如何巧妙地协调资源,确保数据的有序流动。让我们跟随内存的脚步,探索那些隐藏在每次点击、每次命令背后的奥秘。
|
15天前
|
监控 开发者
深入理解操作系统:内存管理的艺术
【10月更文挑战第2天】在数字世界的幕后,操作系统扮演着至关重要的角色。本文将深入探索操作系统的心脏——内存管理,揭示它是如何协调和管理计算机的宝贵资源。通过浅显易懂的语言和生活化的比喻,我们将一起走进内存管理的奥秘世界,了解它的原理、机制以及为何对整个系统的性能和稳定性有着不可替代的影响。无论你是技术新手还是资深开发者,这篇文章都将为你打开新的视角,让你对日常使用的设备有更深层次的认识和尊重。
|
16天前
|
缓存 算法 调度
深入浅出操作系统:从进程管理到内存优化
本文旨在为读者提供一次深入浅出的操作系统之旅。我们将从进程管理的基本概念出发,逐步深入到内存管理的复杂世界,最终探索如何通过实践技巧来优化系统性能。文章将结合理论与实践,通过代码示例,帮助读者更好地理解操作系统的核心机制及其在日常技术工作中的重要性。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开一扇通往操作系统深层次理解的大门。
|
18天前
|
存储 算法 C语言
MacOS环境-手写操作系统-17-内存管理算法实现
MacOS环境-手写操作系统-17-内存管理算法实现
27 0
|
18天前
|
Java C语言 iOS开发
MacOS环境-手写操作系统-16-内存管理 解析内存状态
MacOS环境-手写操作系统-16-内存管理 解析内存状态
23 0
|
18天前
|
存储 算法 C语言
MacOS环境-手写操作系统-15-内核管理 检测可用内存
MacOS环境-手写操作系统-15-内核管理 检测可用内存
27 0
|
1月前
|
Python
python对电脑的操作,获取几核,获取操作系统,获取内存
python对电脑的操作,获取几核,获取操作系统,获取内存