深入解析分段与分页

您所在的位置:网站首页 请求分页技术 深入解析分段与分页

深入解析分段与分页

#深入解析分段与分页| 来源: 网络整理| 查看: 265

分段、分页 引言 什么是碎片? 段式模型的前身:基址加界限寄存器(动态重定位) 分段式管理 分段思想 分段地址转换 段的另一个优点:很好的支持共享 虚拟地址翻译太慢? 段的缺点:过多的外部碎片 分页式管理 分页思想 分页地址转换 分页的缺点:页表过大怎么办? 多级页表 段页式存储 总结:

文章已收录我的仓库:Java学习笔记与免费书籍分享

分段、分页 引言 什么是碎片?

碎片分为内部碎片与外部碎片,都是指浪费而不能使用的空间。

内部碎片是指**已分配但未被使用的地址空间。**例如在64位空间内,你只适用7字节但由于内存对齐不得不为你分配8字节空间,这就产生了1字节内部碎片。

外部碎片是指**未分配且未使用的地址空间。**例如,你申请4字节的Int类型,再申请8字节的long类型,为了内存对齐,其中4字节无法装入8字节类型,这就产生了4字节的外部碎片,如下图所示。

内部碎片是已被分配的空间,是操作系统不可利用的空间;外部碎片是未被分配的,是可分配的,但该空间过小(碎片的含义)无法装入资源,导致不可利用,但外部碎片是可解决的,可以将多个外部碎片紧凑成一个大的空闲空间,但这需要大量成本。

外部碎片

段式模型的前身:基址加界限寄存器(动态重定位)

要想理解分段与分页,必须先谈谈早期的虚拟内存模型。

在经历了纯物理地址后,科学家们期望解决这种内存模型难以统一的问题,于是虚拟内存技术孕育而生,但困扰科学家们的是,如何将虚拟地址转换成物理地址。

早期的科学家们很容易的想到将整个程序作为一个整体,并为每个进程分配一个基址寄存器和界限寄存器,基址寄存器存放该虚拟地址在实际物理地址的起点,而界限寄存器则用以判定程序是否访问非法地址。

通过这种方式,实际的地址很好计算:

实际地址=虚拟地址+基址实际地址 = 虚拟地址 + 基址实际地址=虚拟地址+基址

但是,这种方式仍然将进程的全部地址空间加载内存中,虽然解决了地址翻译问题,但仍然产生了大量的内部碎片,如下图中该进程栈堆区很小,于是在栈堆区之间产生了内部碎片。

image-20210912181943123

从图中可以看出,如果我们将整个地址空间放入物理内存,那么栈和堆之间的空间并没有被进程使用,却依然占用了实际的物理内存。因此,简单的通过基址寄存器和界限寄存器实现的虚拟内存很浪费。

另外,我们必须要确保内存足够放得下进程的虚拟地址空间,但通常主存成本是比较昂贵的,不如磁盘廉价,这种方式通常不支持大的虚拟地址,如果剩余物理内存无法提供连续区域来放置完整的地址空间,进程便无法运行。例如现在32位的进程空间通常是4GB,主存根本就装不下几个进程。

关键问题是:怎样支持大虚拟地址空间,同时栈和堆之间(可能)有大量空闲空间?在之前的例子里,地址空间非常小,所以这种浪费并不明显。但设想一个32位(4GB)的地址空间,通常的程序只会使用几兆的内存,但却需要一次性的将整个地址空间都放在内存中。

我们需要更复杂的机制以利用物理内存,避免内部碎片,早期的科学家们想出了分段这种思想。

分段式管理 分段思想

分段思想其实就是将基址加界限的概念泛化,在上述例子中,我们为代码、堆和栈段分别维护一个段基址加段界限寄存器,样我们不必要每次都强制的装入整个进程空间,每个基址寄存器存放该段在物理地址的实际空间,界限寄存器仍然用于保护地址空间。

image-20210912185535856

现在对于程序未使用的空间,我们没必要为其分配了(注意:我们仍需要为堆预留较多空间,除了堆段,其余段空间都是在编译器就确定了),这样便大大增加了内存的利用率,此外我们发现可以离散的分配空间,即物理内存中的地址不必要是连续的,这也能大大的提高对物理地址的利用率。

分段地址转换

分段地址转换与基址加界限的思想大同小异,在分段思想中,程序可能具有多个段,操作系统通过一个段表来维护各段信息:

段表的地址是操作系统维护的,段表项主要维护段长和段基址,段基址指该段在物理内存中的起始地址,那么该段中的虚拟地址对于实际的地址即为 段基址 + 段内偏移。

分段系统的逻辑地址结构是由段号(段名)和段内地址(段内偏移量)所组成。

例如,若系统是按字节寻址,用3二进制位表示逻辑地址,如果段号占和段内地址各占16位,那么它的逻辑地址结构图如下所示。

那么我们读取前16位作为段号,后16位作为段内偏移,操作系统通过计算 addr = 段号 * 段表项大小 + 段表地址得出对应的段表项地址,通过查询该段表项得出段基址,通过计算 段基址 + 段内偏移得出物理地址。

你可能发现了,在虚拟地址中,每个段的起始地址都是固定的,每个段的总大小都是固定的,其大小为:

size=2p字节,p=段内地址的位数size = 2^p字节,p = 段内地址的位数size=2p字节,p=段内地址的位数

如下图所示,注意展示的是虚拟地址的空间:

image-20210912204532734

此外,栈地址是反向增长的,因此段表中必须维护一个比特位,描述是否为栈段。

段的另一个优点:很好的支持共享

随着分段机制的不断改进,系统设计人员很快意识到,通过再多一点的硬件支持,就能实现新的效率提升。具体来说,要节省内存,有时候在地址空间之间共享(share)某些内存段是有用的。尤其是,代码共享很常见,今天的系统仍然在使用。

为了支持共享,需要一些额外的硬件支持,这就是保护位(protection bit)。基本为每个段增加了几个位,标识程序是否能够读写该段,或执行其中的代码。通过将代码段标记为只读,同样的代码可以被多个进程共享,而不用担心破坏隔离。

为什么页不行?纯页式管理中,一个页是比较大的,页内毫无任何逻辑信息,因此可能放置任何代码,因此我们必须还要确定哪些代码是用于共享,这增加了成本。

因此我们常说,段式管理是符合用户逻辑的,是利于保护和共享的。

虚拟地址翻译太慢?

我们每次翻译一个虚拟地址都需要去找寻段表中的段表项,相当于多义词地址访问,这太慢了!解决方案是为计算机设置一个小型的硬件设备,将虚拟地址直接映射到物理地址,而不必再访问段表。这种设备称为转换检测缓冲区 (Translation Lookaside Buffer,TLB),有时又称为快表。

快表是一个小的高速缓存,现代操作系统无论是分段还是分页中都利用了这种软件技术,有关于快表地址翻译的问题我们将在专门针对地址翻译的文字讲解。

段的缺点:过多的外部碎片

分段可以避免产生内部碎片(不是绝对的),但由于分段是离散的在主存内找到空闲的槽块并插入,问题是物理内存很快充满了许多空闲空间的小洞,因而很难分配给新的段,或扩大已有的段 —— 大量外部碎片。

例如4kb的空间装入3kb的段,产生的1kb的空间无法在装入任何段,产生碎片的主要原因是因为分段使用的大小是不确定的。

当然前面也提到过,外部碎片可通过紧凑的方式以合成较大的空闲空间,但这需要大量成本,操作系统难以维护。

这种情况下,分页式管理应运而生。

image-20210912210836008

分页式管理 分页思想

对于分段式的管理,一段时间后主存上将会遍布大大小小的外部碎片,操作系统难以进行维护,分段的思想是将内存空间分割成不同长度的分片,由于长度不是固定的,产生外部碎片是必然的,之前提到的将整个程序一起装入的方法虽然不会产生外部碎片,但会产生巨大的内部碎片,我们需要更细粒度的划分,以减少内部碎片的产生,解决这一问题的办法是将空间分割成较小的、固定长度的分片,这就是分页式管理。

分页式管理将程序资源划分为固定大小的页,将每一个虚拟页映射到物理页之中,由于每个页是固定大小的,操作系统可以整齐的分配物理内存空间,避免产生了外部碎片,例如一个页大小是4kb,而主存是40kb,操作系统稍加管理便能确保无论何时都能整齐的装入10个页面。

image-20210913101838707

要注意到页在物理内存中也不是连续存在的,进程未使用的页也没必要为其分配内存,通过这种方式我们就解决了由分段产生大量外部碎片的问题,同时由于页较小,只有在已使用的页才会产生少量的内存碎片,这也是可以接受的,目前来看,分页是一个良好的解决办法。

image-20210913102317080 分页地址转换

正如同分段一样,分页地址转换也需要基址+页内偏移来完成,在分段中采用段表来存储段基址,而在分页中则采用页表来存储页基址,页基址表示页在实际内存中的起始地址,那么实际的地址:

addr=页基址+页内偏移addr = 页基址 + 页内偏移addr=页基址+页内偏移

页表是由操作系统维护的,操作系统知道页表的起始未知,页表项的大小是固定的,在32位地址空间中,通常是8字节,这64比特中不仅存储了页基址,还存放着一些其他重要的数据,如:有效位、可读位、脏位等。

虚拟地址是由页表号 + 页内偏移组成的,这与分段中的虚拟地址类似,我们来进行一个简单的计算以得出32位程序中页表号所占用的位数,其中一个页表的大小通常是4kb,那么:

虚拟地址的总空间大小=232=4GB页表项的个数=4GB4kb=2204kb=212byte虚拟地址的总空间大小=2^{32}=4GB\\ 页表项的个数=\frac{4GB}{4kb}=2^{20}\\ 4kb=2^{{12}}byte虚拟地址的总空间大小=232=4GB页表项的个数=4kb4GB​=2204kb=212byte

要能表示220个页表项,我们必须分配20位地址,剩余12位代表页内偏移,即下图中p=12,n=32,我们通常称虚拟页号为VPN,而称页偏移量为VPO,如下图所示:

image-20210913115545830

为什么直接取VPO就代表了页偏移量?这也是很好理解的,因为:页偏移量 = 虚拟地址 - 页起始地址,而页起始地址其实是固定的,即当VPO位全为0时,为对应页的起始地址 ,此时 虚拟地址 - 页起始地址 即为VPO表示数值。

现在操作系统取出虚拟地址,我们设其为vAddr,便可以通过如下步骤翻译成物理地址:

获取VPN与VPO,即VPN = vAddr & 0XFFFFF000;VPO = vAddr & 0X00000FFF; 获取页表项地址,页表项地址=页表起始地址+VPN×页表大小页表项地址 = 页表起始地址 + VPN × 页表大小页表项地址=页表起始地址+VPN×页表大小; 从该页表项内取出页基址,即实际物理起始地址PPN(注意这里仅有20位,需要右移动12位才是真正的地址); 将PPN与VPO连接起来,即真实地址=(PPN


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3