寻址方式:
-
物理寻址
-
分段寻址
-
虚拟寻址
-
分页寻址:引申出多级页表
起源:
寻址方式的发展取决于CPU位数和内存大小,16位就用物理分段寻址,32位用虚拟分段寻址或者2级分页寻址,64位一定用4级分页寻址了
CPU的位数决定了:
-
寻址能力:能够直接寻找地址的范围,比如16位的cpu只能找到从0到2的16次方之间的地址
-
数据处理能力:一次性处理的数据量,如32位cpu,能一次性处理32位的数据(4字节)
-
软件兼容性:64位CPU可以运行32位的软件,反之不行,原因是寻址能力的问题和向下兼容的设计问题(64位在设计时支持向32位兼容)
物理寻址
定义:CPU直接通过地址总线上传输的物理地址访问内存。对应的物理地址在通过数据总线把地址里的数据传回来
总结:直接寻址快,虚拟地址直接映射到物理地址,不需要页表,但是不安全,因为能够随意访问任意物理内存区
物理寻址优点:
查找快,o1
缺点:寻址能力有限:32位CPU最大支持4GB内存,64位CPU理论支持更大内存,但实际受限于硬件。
2.不安全,没有权限保护,程序随便访问内存中随意区域
3.一个程序占用一个内存,无法多程序执行(后面的虚拟内存不占用实际内存才可以实现多程序一起运行)
分段寻址:
起源:为了解决16位CPU无法访问更多内存地址的问题。
定义:分段:逻辑划分多个不同长度的段,如数据段、代码段。分段机制下的虚拟地址由段号和偏移量构成(由两个寄存器存储)
分段寻址基本原理:
把一个总内存分成若干个段,每个段各有段基址寄存器和ip偏移量寄存器。
段基址寄存器访问段的初始地址
ip寄存器存储段内下一个数据的偏移量
虚拟地址转换物理地址:想访问某个段内的数据,只需要段基址寄存器左移<<n位比特位(如16位CPU要左移4位才能得到和20位地址一样的大小的地址),再加上ip寄存器的偏移量就是要访问的数据的地址。
总结:分段机制下的虚拟地址由段号和偏移量构成(由两个寄存器存储)
16位处理器访问20位地址的分段寻址示例:
16位处理器需要的寄存器:
-
有8个通用寄存器,通过ALU数据总线来传输数据给ALU执行单元EU。8个通用寄存器存储数据段和栈段内下一个数据的偏移量,用于获取即将访问的数据的地址
-
CS段寄存器:指针指向代码段的起始地址(基地址)
-
DS段寄存器:指针指向数据段的起始地址
-
SS段寄存器:指针指向栈段的起始地址
-
ES段寄存器:用于额外的数据存储,通常用于字符串操作等
-
IP寄存器(和CS段寄存器结合使用):存储正在执行的代码段(和CS段寄存器结合使用)内的下一个数据的偏移量,用于获取即将访问的数据的地址
每个段寄存器都是一个指针,指向内存中的特定段的基地址。
以E开头的寄存器一般是32位,以R开头的寄存器一般64位,其余的一般16位
总结以上基于物理地址的分段寻址:
-
段寄存器是真实存储地址的,所以这就是物理地址
-
而基于虚拟地址的分段寻址在下面,是通过一系列计算得到的对应物理地址。所以16位CPU一般是基于物理地址来寻址的,32位CPU一般是基于虚拟地址寻址的。
-
分段机制下的虚拟地址由段号和偏移量构成(由两个寄存器存储)
分段寻址下处理器访问内存的步骤:
-
地址生成:地址加法器把通用寄存器的偏移量加上段基址寄存 器的值,再加上IP寄存器的值加起来,就可以通过地址总线访问内存
-
读取数据:内存根据有效地址通过数据总线传递指令到指令队列缓存器
-
指令执行:缓存器再将指令发送给执行单元EU,控制逻辑决定如何将指令传递给算术逻辑单元(ALU)进行执行。
分段寻址优缺点:
-
优点:
-
扩展了寻址能力,使16位CPU能够访问超过其自身地址范围的内存。
-
提供了一定程度的内存保护,通过段寄存器限制内存访问区域。
-
-
缺点:
-
内存碎片化:不同段大小不一,内存分配和释放不灵活,容易产生空洞和碎片。
-
当某个段被释放时,它所占用的内存块会变为空,它与其他段之间可能会形成“空洞”。这些空洞可能无法被后续新分配的段完全利用,从而导致内存碎片化加剧。
-
以上2点都会导致碎片化内存无法使用,使用率降低,如碎片化内存加起来有60MB,但存不了50MB的数据
-
-
已被现代CPU淘汰或简化,主要作为过渡技术。
虚拟寻址:
定义:程序使用虚拟地址访问内存,操作系统通过页表将虚拟地址转换为物理地址。
铺垫1:
虚拟地址就是经过一系列规则计算得出的唯一的与物理地址相对于的地址,物理地址是在内存中的真实存在的地址
虚拟地址转为物理地址叫地址转换
铺垫3:
虚拟地址的作用:
-
灵活管理:与内存访问的实际操作和实际存储位置解耦,使得对内存进行更灵活的管理,如分配、回收、保护
-
地址空间隔离:使每个进程互不干扰对方的内存空间
-
抽象化:让程序员更方便的编写程序,不需要关注物理硬件的细节(要用硬件的哪里的物理地址比较好呢?),使得程序在不同的系统上都能运行,无需修改代码
铺垫2:
linux的实模式:最多访问2的20次方1MB的内存,直接访问任意段的任意物理内存,没有操作系统的概念,没有段的概念,没有权限保护
缺点:如果能访问的内存越大,就越危险
虚拟寻址基本原理--实模式转为保护模式
linux的实模式转为保护模式的过程:就是虚拟地址转为物理地址的过程--即使虚拟寻址又是分段寻址
- 8个通用寄存器的位扩展为32位
- 指令指针寄存器(IP寄存器)扩展为32位
- 为了保护不能随意访问内存某个地方,段寄存器不扩 展(还能够兼容实模式的16位地址),段寄存器实际存储 的是16位的段选择子(一系列序号),包括(描述符表的段号索引,以及使用GDT还是LDT等),所以段选择子可以找到段表里的段
- 而描述符表(段表)由段表内存基地址寄存器GDTR(指向全局段 表)和LDTR(指向局部段表)存储在内存中, 描述符表(段表)中有段号,段基地址,权限等
关键机制:
TLB:高速缓存最近访问的页表项,提高地址转换效率。又叫虚拟寻址的缓存
页表:维护映射关系
缺页中断:当访问的虚拟页未加载到物理内存时触发,操作系统负责加载页面。
虚拟寻址的优点:
-
虚拟内存独立空间:每个进程有独立的虚拟地址空间,确保一个进程不会影响到另一个进程的数据,增强了内存隔离和安全性。
-
重点:因此就可以实现内存中同时存储多个程序
-
权限控制:安全性更高,当触发权限异常,MMU会触发异常,让操作系统来终止该进程
-
内存保护机制:段内的数据偏移量都是已经有的,可以防止内存越界访问
-
支持内存分页和交换,允许使用比实际物理内存更大的虚拟内存。
问题一:64位系统的最大内存容量为256TB是如何做到的?硬件也不可能这么大?
答:这是根据逻辑来计算的地址空间大小,实际上该内存区是从0到最大内存的虚拟地址(就是有序的数字而已),并不是硬盘实际物理地址空间,所以虚拟地址再大都无所谓,后面都要通过计算存入物理空间的
问题二:所有的用户空间都被一个程序占据了吗?
答:因为是虚拟地址,所以可以重复使用地址不会产生冲突(一个程序使用从0到最大地址之间的虚拟地址,另一个程序也可以使用从0到最大地址之间的虚拟地址,会由操作系统转化为不同的物理地址),所以无数个程序都能使用该内存布局的虚拟地址
总结虚拟地址:
-
是有序的数字,可以无数个程序重复叠加使用,由操作系统而不是用户转换为唯一的不同的物理地址
-
虚拟地址的大小可以无限大,当虚拟地址空间被程序填满时,操作系统并不会将所有虚拟地址映射到物理内存中,而是采取措施
具体措施:
当虚拟地址空间被程序填满时,操作系统并不会将所有虚拟地址映射到物理内存中,而是采用以下机制:
-
页面交换:操作系统将虚拟地址划分为固定大小的页面(通常为4KB),并将物理内存划分为相应的页面框。只有当前正在使用的页面会被加载到物理内存中,其他页面则存储在硬盘上的交换空间中。当需要使用这些被换出的页面时,操作系统会进行页面调度置换,将某些不活跃的页面换出到硬盘,让出空间供新的页面加载。
-
按需分页:只有在程序实际需要访问某个页面时,操作系统才会将该页面从磁盘加载到物理内存中。这种方式有效利用了内存,只在需要时才分配内存资源。
-
虚拟内存映射:每个进程都有自己的虚拟地址空间,操作系统通过页表将虚拟地址映射到物理地址,从而实现多程序并行运行时的内存隔离和保护。
-
分页寻址
定义:将虚拟内存和物理内存划分为固定大小的页面,为2的幂大小(如4KB、4MB),通过页表进行地址映射。分页机制下的虚拟地址由页号和页内偏移量组成
术语:
页帧=物理页
虚拟页没有别名,不叫页帧,就叫虚拟页
虚拟页号等同于段号
页表等同于段表
页数:物理页或虚拟页的数量,即有多少个
分页寻址的地址转换过程:
-
把一个内存均等分成若干个页
-
假设每页的大小为xxKB或MB
-
计算有多少物理页和虚拟页:第一种算法:根据内存总大小除以一个页的大小。第二种算法:2的m次方字节=每页的大小,那么总页数就是2的(几位机器-m位)次方的页数,如32位机器就是2的(32-m)次方的页数
-
翻译虚拟地址为物理地址
分页寻址的基本原理举个例子:
-
内存的总大小可以为4kb(12位系统),8kb,16kb(14位系统)等 ,页帧的大小为2的任意次方数
-
比如12位内存总大小为4kb,把内存布局均分为相等的页
-
假设每页的大小为64字节,就可以计算出虚拟页和物理页的页数(4kb除以64字节得到64个物理页数),虚拟页数的计算同样(不过虚拟地址的位数一般高于物理地址,但页帧大小要一致,页数可以不一致)
-
操作系统维护一个页表(数组),由页基址寄存器指向,页表里有虚拟页号,有效位(1表示虚拟页有对应的物理页,0表示无),页帧号(对应的物理页的位置)
-
地址翻译:虚拟地址转为物理地址
分页寻址的地址转换计算规则:
注意:物理页内偏移量 = 虚拟页内偏移量
-
找出虚拟地址对应的虚拟页的位置:把一个虚拟地址转为2进制,然后一分为二分成两部分, 后n个2进制位是页内偏移量,2的n次方 = 每页的大小 (这么做的目的是这n个2进制位刚好能访问完内存中的所有的页数),剩下的前面的二进制位为虚拟页号 ,数据的地址就是虚拟页号的地址加上页内偏移量 或者也可以,前面m个bit位是虚拟页号,2的m次方 = 内存总大小,剩下位是页内偏移量 , 这两个都一样的计算方式
-
找出虚拟页对应的物理地址:,页表根据虚拟页号找到对应的物理页的地址;然后一分为二分成两部分,然后和上一步一样,把物理地址转为2进制,后面几位和虚拟页内偏移量的bit位数和值相同,剩下的前面的2进制位是物理页号(因为刚好能访问所有的物理页)
地址翻译原理:软件+硬件结合
CPU通过寄存器拿到虚拟地址,然后通过MMU进行地址翻译,MMU会去页表基址寄存器拿到虚拟地址对应的物理地址(页表基址寄存器指向页表),然后得到物理地址就去内存中访问数据
MMU(内存管理单元专用硬件)、寄存器、CPU都属于CPU芯片内部的硬件
页表是由操作系统(软件)维护的
分页寻址优点:
-
简化了内存管理,提高了内存的利用率。
-
支持虚拟内存的页面置换,允许程序使用超过物理内存容量的内存。
-
结合TLB提高地址转换效率。
多级页表:
-
单级页表:简单直接,但在大内存系统中页表巨大,浪费内存。
-
多级页表(如两级、三级、四级页表):按层次分割页表,减少内存开销,提高页表管理的效率。
-
倒排页表:减少页表大小,通过物理页索引虚拟页,实现更高效的内存管理。
单级页表引出问题
以上的一级分页只能优化分段寻址的碎片化内存问题,还不能优化页表所占用的内存,以下多级分页是解决这个页表所占用内存太大的问题,每个程序都有页表,那多个程序的页表内存不就超标了吗
多级页表:
定义:按层次分割页表,减少内存开销,提高页表管理的效率。
铺垫:
32位的页表有多大?
假设每页是4kb大小,那么有2的20次方个页数
页表的信息用有效位(占1个bit位),对应的物理页号(占20bit位,因为虚拟页数的大小肯定>=物理页数大小),权限(占3bit),其他信息(占8bit位),所以页表的每一行都占32bit即4字节,那么整个页表的总行数相加即2的20次方乘4字节为4MB
问题:每个程序都有虚拟内存,都有一个页表,那运行100个程序就耗费100个页表也就是400MB内存是不现实的
那么如何压缩页表在内存中的大小?
-
共享页表:在多进程中,某些相同的代码(如共享库)可以被多个进程共享。这些代码段只需要在物理内存中存储一份,并通过页表映射给所有使用它的进程,减少页表的空间需求。
-
多级页表:在较大地址空间的系统中,使用多级页表(如二级页表、三级页表)可以显著减少页表所需的内存。多级页表只在需要时才分配页表项,从而避免为每个可能的虚拟页面都分配页表项。这种结构可以有效减少内存消耗,尤其是在稀疏地址空间中。
-
倒排页表:倒排页表是一种只存储物理页面的页表结构,每个物理页面对应一个条目,而不是每个虚拟页面都有一个条目。这种方法在某些情况下可以显著减少页表内存的占用。
-
临时和优先页面:操作系统通常只在一个时间段内维护活跃的页表项。对于不活跃的进程,系统可以选择释放其页表空间或将其置于磁盘上的整页文件中。这样,只有当前活跃的进程才占用物理内存中的页表。
-
内存映射文件:在使用内存映射文件的情况下,页表可以直接映射到文件的某一部分,减少了系统为每个进程分配独立的页表的需要。
多级页表基本做法:假设32位系统
页表里的一行字段信息 = 页表项
有多少个虚拟页,页表里就有多少个页表项(行--页数)
把这个一级页表4MB的每一页分成一个物理页大小的子页表4KB,共分成1024张子页表,页目录(也叫一级页表)有1024张子页表分别指向1024个页表项,一个页目录基地址寄存器指向页目录
因为一个页表里一行字段信息(页表项)占4字节,所以每个子页表4MB里又有1024个页表项(页数)。
可以一直分页下去,三页四页五页
总结:虚拟页数量对应页表的页表项数量;页表的每页按照物理页大小分成x张子页表,其指向子页表地址的指针存储在页目录中;子页表里有x张页表项数据
页面大小的选择
页面大小影响了内存利用率和地址翻译的效率。较大的页面减少了页表的大小和TLB未命中的概率,但可能增加内部碎片。
问题1:怎么确定某个页表项在哪个子页表中
有一个页目录基地址寄存器指向页目录(一级页表)存储着1023张页,页目录里每一页指向每一个子页表(二级页表)
所以找的时候,先从页目录找子页表,再从子页表里找页表项
问题2:为什么二级页表比一级页表多了一个页目录,还可以降低占用内存?
一级页表有很多未分配的页面都会在页表里占用内存
而二级页表中未分配的页面不会占用页表的内存,就会大大降低空间占用,也就是图中的话:如果一级页表里一行字段信息为空,那么相应的二级页表就不会存在
二级分页寻址:支持32位系统
虚拟地址如何转换为物理地址:
- 虚拟页内偏移量:把虚拟地址转为2进制32个bit位,虚拟页的大小 = 2的m次方,则虚拟地址的后m个bit位就是虚拟页内偏移量 = 物理页内偏移量
- 页目录(一级页表):因为页目录有1024个页数,1024=2的m次方,所以高位(从左往右)m个bit是一级页表(页目录)指向的二级页表中的二级页表号(子页表号)
- 子页表(二级目录):然后剩下的bit位就是二级页表里的页表项的索引号,根据索引号找到物理地址
- 找到物理地址:然后物理地址的帧内偏移量即后m位和第一步的后m个bit位是一样的,和虚拟页内偏移量相同,然后剩下的前面的位是该物理地址16进制转为2进制后的数,两部分拼接在一起得到最终的物理地址
总结:页目录找到二级目录再找到物理地址
四级页表:64位系统需要
页全局目录找到页上级目录找到页中间目录,找到页表项,找到物理地址
分页寻址下CPU访问内存的步骤:软件+硬件实现
TLB也叫快表,他的硬件结构和CPU高速缓存一样,不一样的是里面每组中含有多个缓存行
-
CPU拿到虚拟地址,经过MMU,MMU会访问TLB,TLB会经过 分页寻址及地址转换 查询这个虚拟地址的对应物理地址:
-
如果没拿到,MMU会访问内存中的页表获取相应的页表项。获取到页表项后,MMU会将页表信息存入TLB缓存页表里,然后再把页表放入MMU的翻译组件里翻译成物理地址
-
如果拿到,放入MMU翻译单元转为物理地址,然后利用这个物理地址,来访问高速缓存或内存
-
-
注意:如果没拿到,且TLB里满了,新拿出的页表项放入TLB里会覆盖一个已经存在的页表项,覆盖哪一个取决于替换策略:可以是LRU(最久未使用页面进行替换)或者LFU(使用频率最低的页面进行替换)
分页寻址下CPU访问内存的详细版本:
TLB:高速缓存最近访问的页表项(里面有物理地址等数据),提高地址转换效率。
-
CPU拿到虚拟地址,分成两部分,一部分是页内偏移量,另一部分是虚拟页号,再把虚拟页号分成两部分TLBT和TLBI用于TLB缓存页表里该虚拟地址所在位置,TLBI是TLB的组索引,TLBT来匹配TLb里的组里的页表项数据段
-
如果TLB命中,即找到对应的物理地址,则把物理地址的后面位加上虚拟页内偏移量(因为物理页内偏移量 = 虚拟页内偏移量),形成最终的真实物理地址
-
如果TLB未命中,则MMU需要去访问内存的CR3寄存器访问页表,并把页表信息存入TLB缓存页表里,然后MMU再翻译成物理地址
-
-
再去高速缓存L1中返回数据,里面也是有很多组,又要把物理地址拆分成很多份,又有一套规则确定那些bit位是组索引等等
-
高速缓存器L1未命中就去L2找,未命中去L3找,再未命中去主存找
分页和分段的区别和联系
相同:
都是非连续的内存管理方式,即不要求占用连续的物理内存空间
都是虚拟地址映射到物理地址的机制
不同:
分段是从用户角度考虑,用于数据保护,所以分段的大小由用户程序决定
分页是从内存利用率考虑,所以分页的大小由操作系统决定
分段会有外部碎片问题,因为段大小可变动和动态内存分配和释放导致
标签:虚拟地址,物理地址,寻址,内存,寄存器,内存地址,CPU,页表 From: https://blog.csdn.net/2301_81335708/article/details/143267257