【操作系统】内存管理——地址空间连续内存分配与非连续内存分配

内存管理——地址空间&连续内存分配与非连续内存分配

  • 一、地址空间
    • 1.1 计算机存储层次
    • 1.2 地址和地址空间
    • 1.3 虚拟存储的作用
  • 二、内存分配
    • 2.1 静态内存分配
    • 2.2 动态内存分配
  • 三、连续内存分配
    • 3.1 动态分区分配
    • 3.2 伙伴系统(Buddy System)
  • 四、非连续内存分配
    • 4.1 段式存储管理
    • 4.2 页式存储管理
    • 4.3 页表
    • 4.4 段页式存储管理

一、地址空间

1.1 计算机存储层次

基本要求: 一个进程需要一块存储时分配,完成工作后收回
基本结构: 首先分为物理存储和逻辑存储。

  • 物理地址(PA, Physical Address) :用于内存芯片级的单元寻址,与处理器和CPU连接的地址总线相对应。
  • 逻辑地址(LA, Logical Address) :CPU执行机器指令时,用来指定一个操作数或者是一条指令的地址。也是用户编程时使用的地址。

计算机的存储层次结构图:
物理存储可以从计算机体系结构的三个重要模块入手:CPU、内存和IO

计算机的存储多层结构图:
  大体的调用关系如下,首先要考虑最为快速的缓存,其存取速度与CPU主频相同。缓存的使用是我们所不能意识到的,因为其依靠硬件实现。但内存和虚存是我们需要在操作系统当中操作的。


操作系统对内存资源的抽象图:
  逻辑地址空间:物理地址如果交由计算机或许是可以方便使用的,但是对于人来说,并不容易记忆理解和使用。我们希望我们各个进程使用的存储都相对性地低耦合,有一个比较清晰的逻辑结构。把线性的物理地址编号转换为抽象的逻辑内存结构的管理机制是MMU(内存管理单元)

内存管理的目标

  • 抽象:逻辑地址空间
  • 保护:独立地址空间
  • 共享:访问相同内存
  • 虚拟化:更大都地址空间

操作系统中的内存管理方式:

  • 重定位(relocation)
  • 分段(segmentation)
  • 分页(paging)
  • 虚拟存储(virtual memory/storage)

1.2 地址和地址空间

地址空间的定义

  • 物理地址空间 —— 硬件支持的地址空间(address:[0, Maxsys])
  • 逻辑地址空间 —— 一个运行在程序所拥有的的内存范围(address:[0, Maxprog])

逻辑地址的生成
主要步骤: 编译、汇编、链接、重定位

  • .c file 函数位置、变量名即逻辑地址。
  • .s file 汇编语言中更加贴近机器语言,但是依然用符号代表变量名字。
  • .o file 机器语言中,起始地址都是从0开始的,把变量名转换为地址。
  • linker把多个.o file变成一个单一的.exe file。
  • .exe file 中,地址已经做了全局的分布,不同的.o file程序的地址在单一程序中已经有各自的定义。
  • .exe file 通过loader放到内存中运行,会有一定的偏移量,所以程序依照偏移量来进行正确数据的访问和指令的操作。

物理地址生成

  • CPU根据指令,查找逻辑地址的物理地址在什么地方
  • 根据什么查找?MMU(内存管理单元)将逻辑地址映射到物理地址

1.3 虚拟存储的作用

1. 外存的缓存: 虚拟内存可作为外存的缓存

  • 常用数据放在物理内存中
  • 不常用数据放在外存
  • 运行的程序直接用虚存地址,不用关注具体放在物理内存还是外存

2. 简化应用编译和加载运行: 每个运行程序具有独立的地址空间,而不管代码和数据在物理内存的实际存放,从而简化:

  • 编译的执行程序链接
  • 操作系统的执行程序加载
  • 共享:动态链接库、共享内存
  • 内存分配:物理不连续,虚拟连续

3. 保护数据: 虚拟内存可保护数据

  • 独立的地址空间使得区分不同进程各自内存变得容易
  • 地址转换机制可以进行可读/可写/可执行的检查
  • 地址转换机制可以进行特权级检查

二、内存分配

运行应用所占内存按存储数据特 征划分成多个段(Segment)
内存分配方式

  • 静态内存分配
  • 动态内存分配
    • 连续内存分配
    • 非连续内存分配

2.1 静态内存分配

参考博文:linux下,程序各个部分对应的段位置,图说 bss段 text段 data段 rodata段 栈 堆

静态内存分配是指编译时的内存分配

  • 包括全局变量、静态变量和代码段
  • 位于全局/静态数据段、常量数据段、代码段

下面从低地址到高地址,先分别说明下每个段的意义:

1.受保护区: 受保护区,是用户不能访问的地址,受系统保护,一般为 0-4k,我们平时写程序的初始化一个指针,

char *p = NULL;

这个NULL就是指向的受保护区,0 地址。

2. 代码段:主要存放用户编写的业务逻辑程序、算法等,这里的地址是绝对地址,因此如果有链接动态库,是放在共享库中的。

3. rodata段: .rodata段 是 只读数据段,比如我们用const修饰的值就是放在这个区域的。

const int a[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

4. data段: .data段 是数据段,存放了已经初始化的全局变量,以及已初始化全局静态变量,已初始化的局部静态变量等。

int g_init_v = 1;
static int static_init_v = 1;
static int static_uinit_v;

初始化的全局变量,已经初始化和未初始化的静态变量都在这个段,初始化的局部静态变量。

5.bss段: .bss段 是未初始化的全局变量以及未初始化的静态局部变量。

int g_uinit_v;

6.堆: 程序员可以用来分配内存的空间,使用malloc分配。

/* mallloc */
int *malloc_v = malloc(10);

在创建进程的时候,会为进程分配多大的堆。

7.共享库: 共享库,在调用了共享库的可执行程序里面,共享库放在这个位置。

8.栈: 栈,程序执行过程中,程序用来存放临时变量和函数返回值的区域(局部变量、函数)。由系统分配和回收,在进程创建的时候,每个进程/线程都有自己独立的栈空间。

9.命令行和环境变量: 就是我们linux的环境变量env,命令行参数,比如

int main(int argc, char *argv[])

这里面argv所带的参数。

利用下面的例子说明

2.2 动态内存分配

动态内存分配是指运行时的内存分配

  • 栈(stack)
    • 局部变量
  • 堆(heap)
    • malloc()函数分配内存
    • free()函数释放内存

1. 堆和栈的内存分配

  • 栈由编译器管理:隐式分配
  • 堆的分配和释放由程序员管理:显式分配

2. 分配大小

  • 栈是由高地址向低地址生长的数据结构,是一块连续的内存,能从栈中获得的内存较小编译期间确定大小
  • 堆是由低地址向高地址生长的数据结构,是一个不连续的储存空间内存获取比较灵活,也较大

3. 动态内存分配函数 malloc()

  • malloc()函数: void * malloc (size_ t size);

    • 申请一块size大小的连续堆内存
    • 函数返回值是一个指针,指向刚分配的内存首地址
    • 如果申请内存失败, 返回一个空指针,即返回值为NULL
  • 动态内存的分配和释放必须成对使用

    • 如果malloc()比free()多,会造成内存泄漏
    • 如果malloc()比free()少,会造成二次删除,破坏内存,导致程序崩溃

4. 动态内存回收函数free()

  • free()函数:void free (void *ptr)
    • 释放指针变量在堆区上的内存空间
    • 不能释放栈上的内存空间
    • free()要与malloc()成对使用

三、连续内存分配

3.1 动态分区分配

1. 连续内存分配: 给应用分配一块不小于指定大小连续的内存区域

内存碎片: 不能被利用的空闲内存

  • 外碎片: 分配单元间未被使用内存
  • 内碎片: 分配单元内部未被使用内存

2. 动态分区分配: 当程序被加载执行时,分配一个进程指定大小可变的分区(块、内存块),并且分区的地址是连续的

操作系统需要维护的数据结构

  • 所有进程的已分配分区
  • 空闲分区(Empty-blocks)

3. 动态分区分配策略
(1)最先匹配(First-fit):在内存中找到第一个比需求大的空闲块,分配给应用程序

(2)最佳匹配(Best-fit):在内存中找到最小、最适合的空闲块,分配给应用程序

(3)最差匹配(Worst-fit):在内存中找到最大的空闲块,分配给应用程序

动态分区方式对比表

动态分区方式原理&实现优势劣势
最先匹配1. 空闲分区列表按地址顺序排序
2. 分配需要寻找一个合适的分区
3. 重分配需要检查是否可以合并相邻空闲分区
简单
在高地址易于产生更大空闲块
外部碎片
分配大块时间较慢
最佳匹配1. 分配需要寻找一个合适的分区
2. 空闲分区列表按照大小排序(从小到大)
3. 重分配需要检查是否可以合并相邻空闲分区
比较简单
可减小外碎片大小
避免大的空闲分区拆分
大部分是小尺寸时高效
外部碎片 & 释放分区慢
易产生很多无用小碎片
最差匹配1. 分配时,选最大分区
2. 空闲分区列表按由大到小排
3. 重分配需要检查是否可以合并相邻空闲分区
避免出现太多的小碎片
大部分分配是中尺寸时高效
外部碎片 & 放分区慢
大空闲块易破碎,大分区无法被分配

三种分配方式并无优劣之分,因为我们无法判断内存请求的大小。

4. 碎片整理
  可以看到的是,三种分区动态分配的方式都会产生外部碎片,因此我们可以对碎片进行一定的整理来解决碎片问题,通过调整进程占用的分区位置来较少会避免分区随便

(1)紧凑: 通过移动分配给进程的内存分区,以合并外部碎片
紧凑条件: 所有的应用程序可动态重定位

(2)分区对换

  • 运行程序需要更多的内存时,抢占等待的程序并回收它们的内存,以增大可用内存空间

  • 早期linux/unix分区对换过程:充分运用内存的做法,只有一个进程可以在内存中运行的状态下,可以对换来实现多进程的交替运行

3.2 伙伴系统(Buddy System)

伙伴系统比较好的折中了分配和回收过程当中合并和分配块位置的碎片问题。

1. 分区大小

  • 可分配分区的大小 2U
  • 待分配分区的大小为2(U-1) < s ≤ 2U,把整块分配给应用
  • 待分配分区的大小为 s ≤ 2(i-1)
    • 将大小为 2i 的当前空闲分区划分成两个大小为 2i-1空闲分区
    • 重复划分过程,直到2(i-1) < s ≤ 2i ,把一个空闲分区分配出去

2. 数据结构

  • 空闲块按大小和起始地址组织成二维数组
  • 初始状态:只有一个大小为 2U的空闲块

3. 分配过程

  • 由小到大在空闲块中找最小可用块
  • 如空闲块过大,对可用空闲块进行二等分,直到得到合适可用空闲块

4. 释放过程

  • 把块放入空闲块数组
  • 合并满足条件的空闲块
    合并条件
  • 大小相同 2i
  • 地址相邻
  • 低地址空闲块起始地址为2i+1的位数

四、非连续内存分配

1. 连续内存分配的缺点

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

能否通过新的一些手段来改善这些情况?=> 非连续内存分配

2. 非连续内存分配的优点

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

3. 非连续内存分配的缺点

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

  • 软件方案(开销相当大)

  • 硬件方案(采用硬件辅助机制)

    ➢ 分段(Segmentation)

    分页(Paging)

4.1 段式存储管理

1. 段地址空间

  • 程序运行的段地址空间由多个段组成
  • 段的概念:段表示访问方式和存储数据等属性相同的一段地址空间
  • 主代码段、子模块代码段、公共库代码段、栈段、堆数据(heap)、初始化数据段、符号表等…

逻辑地址空间转化为不连续的二维结构:

2. 段访问机制
段的概念:

  • 段表示访问方式和存储数据等属性相同的一段地址空间
  • 对应一个连续的内存“块”
  • 若干个段组成进程逻辑地址空间

段访问: 逻辑地址由二元组(s,addr)组成

  • s——断号
  • addr——段内偏移

段访问的硬件实现:

4.2 页式存储管理

1. 页式存储管理概念
页帧(帧、物理页面,Frame, Page Frame)

  • 把物理地址空间划分成大小相同的基本分配单位
  • 2的n次方,如512,4096,8192

页面(页、逻辑页面,Page)

  • 把逻辑地址空间也划分为相同大小的基本分配单位
  • 帧和页的大小必须是相同的

页面到页帧:

  • 逻辑地址到物理地址的转化
  • 页表
  • MMU/TLB

2. 页式存储管理的地址转化
页帧(Frame)
内存物理地址的表示:二元组(f,o)

  • frame-number:帧号(F位,共有 2F 个帧
  • off-set:帧内偏移(S位,每帧有 2S 字节)

物理地址 = f * 2s + o

物理地址计算实例:

  • 假定16-bit的地址空间
  • 9-bit(512 byte)大小的页帧
  • 物理地址表示 = (3,6)

物理地址 = f * 2s + o = 3 * 2 9 + 6 = 1542

其中:
F:帧号的位数
s:一帧的长度
f:帧号
s:帧内偏移

页面(Page)

  • 页内偏移 = 帧内偏移
  • 通常:页号大小 ≠ 帧号大小

进程逻辑地址的表示:二元组(p,o)
p —— 页号(p位,2p个页)
o —— 页内偏移(S位,,每页有2S字节)

虚拟地址 = p * 2s + o

3. 逻辑地址到物理地址的映射 >>>>>>>>>页表
页表:

  • 页到帧的映射
  • 逻辑地址中的页号是连续的
  • 物理地址中的帧号是不连续的
  • 不是所有的页都有对应的帧

页表:保存了逻辑地址——物理地址之间的映射关系

4.3 页表

(1)页表概述
页表结构

  • 每个进程都有一个页表
  • 每个页面对应一个页表项
  • 随进程运行状态而动态变化
  • 页表基址寄存器

页表地址转化实例

(2)分页机制的性能问题 : 空间代价、时间开销

  • 访问一个内存单元需要2次内存访问
    ➢ 一次用于获取页表项
    ➢ 一次用于访问数据

  • 页表可能非常大
    ➢ 64位机器如果每页1024字节,那么一个页表的大小会是多少?(264 / 210 = 254
    ➢ 每一个运行的程序都需要有一个页表

  • 如何处理?
    ➢ 缓存(Caching)
    ➢ 间接(Indirection)访问

2. 快表
利用缓存的机制来减少对内存的访问,实际上就是把近期访问过的页表项缓存到CPU里

  • TLB 使用关联存储实现,具备快速访问性能
  • 如果TLB命中,物理页号可以很快被获取
  • 如果TLB未命中,对应的表项被更新到TLB中

3. 多级页表
通过简介引用的方式来减少页表的长度,通过间接引用将页号分成k级

  • 访问次数 k+1 次
  • 具体访问过程:第一级页表项作为第一页表的偏移找到第二级页表上的起始,第二级页表项作为第二页表的偏移找到第三及页表上的起始…,最后找到访问内存单元的物理页号 + 物理内存的访问
  • 减少每级页表的长度

二级页表实例:

4. 反置页表

  • 对于大地址空间(64-bits)系统,多级页表变得繁琐
  • 页寄存器和反置页面的思路
    • 不让页表与逻辑地址空间的大小相对应
    • 让页表与物理地址空间的大小相对应

4.4 段页式存储管理

段式存储在内存保护方面有优势,页式存储在内存利用和优化转移到后备存储方面有优势。

在段式存储管理基础上,给每个段加一级页表

通过指向相同的页表地址,实现进程间的段共享

整理自 【清华大学】 操作系统

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/833664.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入浅出(五)JsonCpp库

JsonCpp库 1. JsonCpp 库1.1 JsonCpp库下载 2. JsonCpp库编译与部署3. C示例 1. JsonCpp 库 JsonCpp 是一个开源的 C 库,用于解析、生成和操作 JSON 数据。它提供了简单易用的 API,使得在 C 程序中处理 JSON 数据变得方便和高效。以下是 JsonCpp 库的一…

适合宝妈的项目,一单20,看似不起眼的小生意,却能闷声发财!

向大家推荐一个小项目,信息差较大,每单至少有20元的利润。目前参与的人还不多,是个不错的机会。操作方法也非常简单,只需利用京东家政券赚取差价。可能很多人是第一次了解到这个——京东竟然还有家政券。 周周近财:让…

Android11 InputReader分析

InputReader线程主要负责读取输入数据,并把数据交给InputDispatcher线程。本文以多指触摸屏为例,梳理一下InputReader的流程。 InputReader线程主要完成以下工作: 处理已有的输入设备处理新增或者移除的输入设备对输入设备产生的输入数据进行…

数字工厂管理系统如何实现生产过程透明化

随着科技的飞速发展,数字化转型已成为制造业不可逆转的趋势。数字工厂管理系统作为实现生产自动化、智能化的重要工具,其在提升生产效率、降低运营成本、优化资源配置等方面的作用日益凸显。其中,实现生产过程的透明化是数字工厂管理系统的重…

一文彻底学会Vue3路由:全面讲解路由流程、路由模式、传参等——全栈开发之路--前端篇(7)路由详解

全栈开发一条龙——前端篇 第一篇:框架确定、ide设置与项目创建 第二篇:介绍项目文件意义、组件结构与导入以及setup的引入。 第三篇:setup语法,设置响应式数据。 第四篇:数据绑定、计算属性和watch监视 第五篇 : 组件…

数据中台:企业数字化转型的驱动力量_光点科技

在当今数字化快速发展的时代,企业正积极寻求转型升级的新路径。在这个过程中,数据中台以其独特的功能和价值,逐渐成为了企业数字化转型的关键驱动力。本文将深入探讨数据中台的角色、架构及其在企业中的应用,以期为企业的数字化转…

[力扣题解]150. 逆波兰表达式求值

题目:150. 逆波兰表达式求值 思路 建一个栈,遍历算式,遇到数字塞进去,遇到算符,去除栈顶2个元素进行运算; 假设算式是9 3 -,栈里的顺序是9 3,(栈顶在右)&am…

01-基本概念- 索引,文档和 REST API

# kibana_sample_data_ecommerce 为es 索引#查看索引相关信息 GET kibana_sample_data_ecommerce#查看索引的文档总数 GET kibana_sample_data_ecommerce/_count#查看前10条文档,了解文档格式 POST kibana_sample_data_ecommerce/_search { }#_cat indices API #查看…

flask 模拟简单的登录功能(2)

通过设置 app.secret_key 进行用户登录验证 通过使用app.secret_key,可以对会话数据进行加密和解密,以确保会话数据的安全性。会话数据包括用户的登录状态、表单数据等敏感信息,通过设置 app.secret_key,可以防止会话被恶意篡改或…

论文查重率高,有什么办法降重吗?

现在大部分学校已经进入到论文查重降重的阶段了。如果查重率居高不下,延毕的威胁可能就在眼前。对于即将告别校园的学子们,这无疑是个噩梦。四年磨一剑,谁也不想在最后关头功亏一篑。 查重率过高,无非以下两种原因。要么是作为“…

CRM(客户关系管理系统)

商机流程 为什么选择简道云CRM? 行业痛点 很多客户有复杂的订单成本计算方式,复杂多变的审批流程,个性化/流程化的数据结构,没有自定义能力就很难满足。 解决方案 在CRM套件的基础上自定义编辑/搭建了适合公司业务的CRMERP 两…

PCIE协议-2-事务层规范

1.事务层概述 从高层次上看,事务层的关键方面包括: 一个流水线化的全分割事务协议用于区分事务层数据包(TLPs)的排序和处理要求的机制基于信用量的流控制可选支持数据中毒和端到端数据完整性检测 事务层包含以下内容&#xff1…

dummy_worker C++ 预占用部分比例cpu资源,人为创造cpu资源紧张

背景 有时候为了C测试程序在cpu资源紧张情况下是否正常&#xff0c;需要人为创造cpu资源紧张 编译方法 g -o dummp_worker dummp_worker.cpp -stdc11 -pthread 使用方法 ./dummp_worker 4 0.2 占用4个cpu核的20%比例的cpu资源 源码 // dummp_worker.cpp #include <c…

Axure RP 9:卓越的交互式产品原型设计工具

Axure RP 9&#xff0c;作为一款备受欢迎的交互式产品原型设计工具&#xff0c;已经在全球范围内赢得了众多设计师和开发者的青睐。这款软件凭借其强大的功能和出色的用户体验&#xff0c;成为了产品原型设计领域的佼佼者。 Axure RP 9支持Mac和Windows两大操作系统&#xff0…

Java并发编程:面经总结

1、描述Synchronized和reentrantlock的底层实现和重入的底层原理 2、描述锁的四种状态和升级过程 3、CAS是什么及ABA问题如何解决 4、请谈一下AQS&#xff0c;为什么AQS的底层是CAS volatile 5、DCL单例为什么要加volatile 6、聊聊你对as-if-serial和happens-before语义的…

Ubuntu22安装docker

安装步骤 1. 更新软件包索引 首先&#xff0c;打开终端并更新你的软件包列表以确保访问到最新的软件包版本&#xff1a; sudo apt-get update 2. 安装必要的依赖项 安装几个必需的软件包&#xff0c;这些软件包让apt能够通过HTTPS使用仓库&#xff1a; bash sudo apt-ge…

茅台申购,多平台签到与通知 | 使用极空间NAS部署一个神级脚本『DailyCheckIn』

茅台申购&#xff0c;多平台签到与通知 | 使用极空间NAS部署一个神级脚本『DailyCheckIn』 哈喽小伙伴们好&#xff0c;我是Stark-C~&#xff0c;今天为大家分享一个极空间上非常实用且好玩的项目。 小伙伴们都知道&#xff0c;目前很多平台为了促进用户的活跃度和黏性&#…

LeetCode--所有质数、质数对

1.0 Q: 输出 100 以内所有质数 1.1 /* 第一层循环控制检查到哪个数* 第二层通过遍历除以每个比他小的数的方式,检查每个数是不是质数* 由于要遍历检查,设置一个标记,只要任意一次循环可以整除,我们就设置该标记为不是质数 */boolean isPrime true;for (int i 2; i < 100…

如何彻底将CAD或者Cadence卸载干净

最近因为升级软件需要先彻底删除这两个软件&#xff0c;发现无论如何都不能卸载干净&#xff0c;于是乎找到这样一个软件帮助卸载或查找剩余的软件残留&#xff1a; 官网&#xff1a;https://geekuninstaller.com 支持软件和 UWP 应用的卸载&#xff0c;查看软件注册表和安装目…

[leetcode] 68. 文本左右对齐

文章目录 题目描述解题方法贪心java代码复杂度分析 题目描述 给定一个单词数组 words 和一个长度 maxWidth &#xff0c;重新排版单词&#xff0c;使其成为每行恰好有 maxWidth 个字符&#xff0c;且左右两端对齐的文本。 你应该使用 “贪心算法” 来放置给定的单词&#xff…