地址空间介绍,虚拟地址空间(划分区域+存在原因+vm_area_struct),虚拟内存管理(延时分配+分页机制+MMU+页表结构),缺页中断,访问物理内存的详细流程,物理内存管理

目录

地址空间 

地址空间结构图 

全局区 

static变量

共享区 

代码区 

引入

fork返回值问题 -- 虚拟地址空间

虚拟地址空间

引入

类比

计算机分配内存就可以看作是上面的过程

介绍

划分区域 

引入

原理 

区域调整

 堆区管理机制 

引入

vm_area_struct 

代码

为什么会有地址空间呢?

内存分布有序化

进程的独立性问题

解决方法

虚拟内存管理机制

引入

 介绍

延时分配

引入

介绍

原理

优点

分页机制

引入

概念介绍

页/页帧/页框

MMU

页表 

页表

访问物理内存的流程

页面置换

管理物理内存 

引入

结构体大小问题

介绍 


地址空间 

地址空间结构图 

  • 我们在学习语言的时候,或多或少的都有见过这张图
  • 我们在 linux下 打印出来的各种类型的变量地址,是很规整的按照上图排列的
  • (windows下可能有偏差)

全局区 

初始化和未初始化数据 -- 都属于全局区

static变量
  • 它其实就被放在全局区
  • 因为它的生命周期是和全局变量一样的,程序结束后才被销毁
  • 它只是比全局变量多了个限制,只能在该函数内部使用

共享区 

  • 它可以被多个进程同时访问和共享,这些进程可以将共享区域用作数据交换的一种方式
  • 当一个进程将数据写入共享区时,其他进程可以读取这些数据
  • 并且在需要时也可以向共享区写入自己的数据

代码区 

  • 下图是三个字面常量:
  • 如果直接编译字面常量,是可以编译通过的
  • 实际上,字面常量是被硬编码进我们的代码区的
  • 代码和字面常量的属性也很像,都是只读
  • 就是因为他俩在同一个区 -- 代码区

引入

fork返回值问题 -- 虚拟地址空间

我们之前以为地址空间就是真的内存分布,但其实不是!!!

  • 我们通过fork返回值,可以发现ret变量有两个值
  • 那就至少说明,这两个父子进程中的ret变量的地址不一样
  • 那也就说明子进程中有自己独立的ret

  • 接下来我们直接用变量值来看看情况
  • 定义一个flag=10,在子进程会进入的if条件中改变它的值:
  • 在子进程中改变该值,居然对应的地址一样 而 值不一样?
  • 如果该地址真的是内存地址的话,那么就不可能出现这种情况,一个地址只会对应一个值
  • 所以,这个地址空间绝对不是实际的内存,而是一个抽象的概念(虚拟地址)
  • 而几乎所有的语言中,对于地址的概念都是虚拟地址

接下来,就让我们重新认识一下地址空间吧 

虚拟地址空间

引入

  • 因为程序只有在运行之后,才会将可执行文件加载到内存中,才会分配空间
  • 因此实际上是 -- 进程拥有地址空间
  • 而这块空间其实只是os给他画的大饼(其实每个进程都认为自己独占系统资源)
类比
  • 一个富翁为自己的几个私生子都说,要将自己名下的财产全给他(例如有1亿),这就是富翁给自己儿子们画的大饼
  • 实际上不会只给一个人,而私生子是不知道这件事的,因为他认为自己是唯一的儿子
  • 在这个前提下, 每个儿子都会向富翁要一部分去使用
  • 富翁就将自己拥有的1亿,按儿子要的数量给了儿子
计算机分配内存就可以看作是上面的过程
  • 地址空间定义了一个进程可以访问的内存范围,以及这个内存范围的布局方式
  • 操作系统负责为每个运行的进程分配和管理独立的地址空间,以确保它们之间的内存互相隔离,互不干扰 -- 也就对应上面给钱的过程
  • 每个进程都认为自己拥有整个系统的内存 -- 对应上面儿子的认知

  • 每个进程都拥有一份地址空间,总得管理吧,一个进程一份呢
  • 管理就得先描述,再组织
  • 由于地址空间不是一个简单的变量可以定义的,因此还是得要个结构体来描述它的属性
  • 由于每个进程都有,这个地址空间其实也就是进程属性的一部分
  • 即: 是描述进程的的结构体的一部分
  • 也就是说,task_struct里面包含了这个结构体(叫做mm_struct)的指针

介绍

  • 地址空间描述的基本空间大小是字节
  • 每个进程都有自己的虚拟地址空间,这是一个连续的地址范围,通常从0开始,一直到操作系统支持的最大地址
  • 虚拟地址空间的大小通常远大于物理内存的大小

  • 如果在32位机下,就有32位数字位,就能构成2^32个不同的地址
  • 而2^32字节==4gb,地址从0000...0000到ffff...ffff

划分区域 

引入
  • 我们一般看到的地址空间都是有分区域的,而且这个区域范围是不固定的(比如堆栈的区域):
  • 这是怎么实现的呢?
  • 和它结构体中定义的属性有关
原理 

先想想,我们平时是如何划分区域的呢?

  • 比如要四六分一个10cm的东西,我拿前4cm,你拿后6cm
  • 而这里的前后又如何具体出来呢?

  • 是不是可以通过坐标去表示?
  • [0,4]是我的,[4,10]是你的 

  •  mm_struct也就是这样实现的,它里面定义了每个区域的起点和终点,从而实现划分区域

    (这是linux中mm_struct的一段代码) 

区域调整
  • 如何进行区域调整呢?毕竟栈和堆是可以增长的
  • 其实只要修改坐标范围就行了
  • 也就是修改结构体中的end or start(多方便鸭,直接改变量值就行辣)

 

 堆区管理机制 

引入
  • 堆区的使用非常零散化
  • 我们动态开辟空间的时候,如果需要的空间很大,它在内存上很大可能并不连续,而是这一点那一点
  • 但我们在使用的时候,只需要拿着起始地址就行,这是为什么呢?
  • 底层为了支持上层的行为,自然是有相应的结构在支撑
vm_area_struct 
  • 用来描述虚拟内存区域的数据结构
  • vm_area_struct 结构记录了虚拟内存区域的起始地址、大小、权限等信息,并通过双向链表的方式组织起来,以便快速查找和操作
  • 可以用来划分虚拟内存(地址空间只是划分出大的区域,小的区域由该结构体划分),并不只在堆区使用
代码

可以看到,它里面存放着内存区域的始末位置,并且以双向链表的形式组织起来 

 

为什么会有地址空间呢?

内存分布有序化
  • 由于地址空间是有序的,但实际申请的物理内存无序,哪里都能放
  • 因此可以使我们看到的内存分布有序化(我们直接操作的都是地址空间)
  • 在实际执行代码时,os会将起始地址交给我们的cpu,但是每次给的都是不同的地址
  • 如果!!!地址可以有序分布,就可以使起始地址在同一个位置存储着,甚至可以使cpu直接读取对应地址,可以提高效率
进程的独立性问题
  • 过去是直接访问物理内存的,而且,内存是随时都可以读写的!
  • 因为会载入代码,所以可以写
  • 但如果出现野指针的话,比如一个进程的指针指向另一个进程块
  • 进程之间就可以互相修改了,太危险啦!!!
  • 这样根本保证不了进程的独立性,随时都有可能被其他进程拿到自己空间的地址,然后就进行读写了
解决方法
  • 引入虚拟地址空间的概念
  • 因为有了地址空间,按照规定的地址范围,就可以按照每个区域不同类型的变量,规定不同的权限
  • 比如你拿着只读区的地址,去进行写入操作,就会报错(这时os可以在你还没有访问到物理内存的情况下,将你非法的行为杜绝掉,因为地址空间是被os创建的!)
  • 可以防止程序错误或恶意软件破坏其他进程的数据

 

虚拟内存管理机制

引入

  • 地址空间只是定义了进程可以使用的虚拟内存地址的结构和范围,不涉及内存的物理实际分配
  • 实际起作用的是虚拟内存管理机制,地址空间是其中的一环

  • 地址空间描述了进程可以使用的虚拟地址的布局和范围,虚拟内存管理是操作系统实现和维护这一虚拟地址空间的机制
  • 在虚拟地址空间中,进程可以访问内存的不同部分,如代码段、数据段、堆、栈等,而这些地址在物理内存上并不是连续的或一一对应的
  • 虚拟内存管理使操作系统能够将虚拟地址空间映射到物理内存,以满足程序的内存需求
  • 还允许多个进程共享相同的地址空间,但各自拥有独立的虚拟地址

 介绍

虚拟内存管理是操作系统的一个关键组成部分,它允许程序以虚拟地址的形式访问内存,而不需要了解内存的物理地址

虚拟内存管理提供了一种抽象层,使内存管理更灵活、安全和高效

 

延时分配

引入
  • 如果申请了物理空间,但不直接使用,是不是就相等于浪费了资源?
  • 我们本可以将这份资源交给更急需要的进程,等该进程将要使用的时候再分配会不会更合理呢?
介绍
  • 由于程序的虚拟地址空间通常比物理内存大得多,不是所有虚拟内存都需要在程序启动时分配物理内存
  • 在延时分配策略下,操作系统并不立即分配物理内存,而是等到程序实际访问虚拟内存中的页面时才分配相应的物理内存
原理
  • 当我们申请空间的请求发给系统,就会在虚拟地址空间上申请新的区域
  • 但不一定会和物理内存进行映射(也就是延时满足空间的需求,先画饼)
  • 当你真正对物理空间进行访问的时候,才会实际执行内存的相关管理算法处理,让你可以使用空间
  • 由os完成,用户无法感知到
优点
  • 节省物理内存:延时分配使得只有真正需要的内存页才分配物理内存,从而节省了物理内存的使用

  • 提高启动性能:程序启动时,不需要为整个虚拟地址空间分配物理内存,这可以加快程序的启动速度。

  • 支持大型地址空间:允许程序拥有非常大的虚拟地址空间,而无需拥有同等大小的物理内存

分页机制

引入

前面说到的虚拟内存管理使操作系统能够将虚拟地址空间映射到物理内存,以满足程序的内存需求

这个映射就是通过页表+mmu实现的

概念介绍

分页是一种内存管理技术,它将涉及到的内存相关结构(虚拟地址空间,磁盘,物理内存)都分块了,一般为4kb

页/页帧/页框
  • 进程的虚拟地址空间分块,称为页
  • 磁盘上存放的可执行文件,编译时就是按照地址空间的排布进行的,然后确定了其逻辑地址,也被划分成了4kb (以4kb大小存放的数据,称为页帧)
  • 物理内存也被划分成4kb,其大小称为页框:
  • 所以将磁盘上的数据加载到内存(也就是io的过程),就相当于将页帧装进页框里
MMU
  • 是位于中央处理单元(CPU)和物理内存之间的硬件组件
  • MMU负责将逻辑地址(由CPU生成)转换为物理地址(用于访问实际的内存单元)
页表 
  • 用于管理虚拟内存和物理内存之间的映射关系
  • 虚拟内存是指操作系统为每个运行的进程提供的抽象内存空间,而物理内存则是计算机实际的硬件内存

 

页表
访问物理内存的流程
  • 程序访问虚拟内存,通过内嵌在cpu中的MMU转换成物理地址,然后就可以进行访问了

那么是如何转换的呢?

  • MMU通过查找页表来拿到物理地址
  • 那么我们需要先构建出页表结构才行(比如使用map存放两个指针)
  • 并且我们可能还需要标识位来标识该地址是否存在在内存中
  • 那就按照一个条目9字节来算,地址编号在32位机下是32bit,我们就需要2^32(约等于4G)个条目,也就是4G*9=36G
  • 我们怎么可能为了存储页表结构就用到36G内存??
  • 所以页表结构并不像我们想象的那样

那页表具体是什么结构呢?

  • 其实我们是将地址编号的32位拆开看待的:
  • 前10位作为一级页表的key值,得到某个二级页表的起始地址
  • 中间10位作为二级页表的key值,得到物理内存中某个页框的起始地址
  • 后12个bit位,恰好可以代表4kb的编号范围(2^12=4kb),所以它作为物理内存中页框的偏移量,用偏移量+页框起始地址,就可以得到实际物理地址了

这样的结构可以解决我们的体积过大的问题吗?

  • 一级页表和二级页表都由10位bit表示,如果全部都申请内存,也就是需要2^20(2^10 * 2^10)个条目,按每个条目10字节来算,需要10*2MB=20MB
  • 后12位我们是直接使用传入的虚拟地址中的数值,不需要额外保存
  • 所以最后的消耗比起之前小了非常多
  • 不仅如此,二级页表我们是按需申请的,一级页表中需要用到的地址有几个,就有几个二级页表
  • (还记得我们的地址空间吗,它也是按需加载到内存中的,并不会全部加载,那得多占内存啊)

如何按需加载?

  • 页表在初始化时,物理地址首先填入的是数据在磁盘中的地址
  • 当进程进行访问时,os查找页表,如果发现该地址并不存在于内存中
  • 会先去物理内存中找到空的页框,然后将对应页帧加载到内存中
  • 然后将页框的起始地址填入页表中
  • 这一过程也被叫做"缺页中断"
页面置换
  • 程序和数据,不止在物理内存中存在,也可以在磁盘的swap区域存在
  • 当物理内存不足时,操作系统需要选择哪些页面从物理内存中移除,以便为新的页面腾出空间
  • 通常使用算法如LRU(最近最少使用)、FIFO(先进先出)或其他算法来决定哪些页面被置换出去
  • 当我们需要的时候,可以重新唤入这块内存

 

管理物理内存 

引入

  • 前面我们说到,物理内存被划分成4kb大小的块
  • 而我们加载到内存是需要查找未使用的块然后填充,也就是说,我们需要标志位,来标记该块是否被使用
  • 除此之外,我们还需要记录该块存放数据的很多信息(当内存不足时,我们会将不常用的数据交换到磁盘上)
  • 所以我们需要一个结构体存储这些信息 -- struct_page

结构体大小问题

  • 由于每个页框都需要对应一个page结构体,假设我们的内存是4G,也就是100w个4kb,也就是100w个结构体
  • 如果该结构体大小过大,那仅仅只是用于管理的数据结构就占用了内存很大一部分,那计算机还运行啥,直接崩掉
  • 所以这个结构体一定要控制它的大小

介绍 

  • 有了相应的数据结构,就该将他们组织起来了
  • 最简单的就是用数组存起来,相当于用下标给这些4kb大小的页框编了号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值