【操作系统导论】内存篇——分页

引入

采用 「分段」 的方式,将空间切成 不同长度的分片,会出现 碎片化 问题,随着时间推移,分配内存会越来越困难。

因此,值得考虑「分页」的方法:

  • 将空间分割成 固定长度的分片

  • 将物理内存看成是定长槽块的阵列,叫作 页帧 (page frame,PF),每个页帧包含一个虚拟内存页。

「分页」具有许多优点:

  • 灵活性

    通过完善的分页方法,操作系统能够高效地提供地址空间的抽象,不管进程如何使用地址空间。

    例如,不用假定「堆」和「栈」的增长方向,以及它们如何使用。

  • 简单性

    假设有一个 64 字节的地址空间,且一个页帧为 16 字节,则只需要在物理地址空间中找到 4 个空闲页。

线性页表

地址转换

为了实现地址转换,需要将虚拟地址看作两个部分:*虚拟页面号(VPN)*和 页内偏移量(offset)

假设进程的虚拟地址空间是 64 字节,页帧大小为 16 字节:

  • 页帧大小 16 字节,对应 2 的 4 次方,则 offset 占 4 位;

  • 地址空间 64 字节,对应 2 的 6 次方,则 VPN 占 2 位。

在这里插入图片描述

转换虚拟地址,只需要将*「虚拟页面号 VPN」替换成「页帧号 PFN」*。

在这里插入图片描述

页表结构

为了记录地址空间的虚拟页在物理内存中的位置,OS 为每个进程保存一个数据结构,称为 页表(page table)

页表不由硬件存储,它通常存放在内存中,甚至可以被交换到磁盘上。

对于下面例子,页表中应该具有 4 个条目:(VP 0 → PF 3)、(VP 1 → PF 7)、(VP 2 → PF 5)、(VP 3 → PF 2) 。

在这里插入图片描述

那么,页表的结构究竟是怎么样的呢?

最简单的形式为 线性页表(linear page table),即一个数组。

操作系统通过「虚拟页面号 VPN」检索该数组,在索引处查找**「页表项 PTE」**,再找到对应的「页帧号 PFN」。

对于一个「页表项 PTE」,具有着许多的位,比如:

  • 有效位(valid bit)

    用于指示特定的地址转换是否有效。

    通过将地址空间中所有未使用的页面标记为无效,则不再需要为这些页面分配物理帧,从而节省大量内存;

    如果进程尝试访问这部分无效空间,就会陷入操作系统,可能会导致进程终止。

  • 保护位(protection bit)

    表明该页是否可以读取、写入、执行。

    同样,以不被允许的方式访问该页,则会陷入操作系统。

  • 存在位(present bit)

    表明该页是在物理内存中还是在磁盘上。

  • 访问位(accessed bit)

    用于追踪页是否被访问,也用于确定哪些页比较受欢迎,应该保留在内存中。

  • 脏位(dirty bit)

    表明该页被带入内存后是否被修改过。

下面是 x86 架构的页表项:

包含了存在位(P),读/写位(R/W),用户/超级用户位(U/S),访问位(A),脏位(D);

(PWT、PCD、PAT 和 G)用来确定硬件缓存如何为这些页面工作,最后是页帧号(PFN)。

在这里插入图片描述

可以阅读「英特尔架构手册」,以获取有关 x86 分页支持的更多详细信息。

硬件 TLB

对于每个内存引用(取指令、显式加载、存储),分页需要执行一个额外的内存引用,以便从页表中获取地址转换。

这使得 额外的内存引用开销大,在这种情况下,可能会导致 系统运行速度减慢两倍或更多

想要加速虚拟地址转换,自然要借助硬件的帮忙,即 地址转换旁路缓冲存储器(TLB),简称 地址转换缓冲

对每次内存访问,硬件先检查 TLB 中是否有期望的转换映射,如果没有,再访问页表。

TLB 带来了巨大的性能提升,实际上,因此它使得虚拟内存成为可能。

基本算法

现在假定使用「线性页表」和硬件管理的「TLB」,则算法的大体流程如下:

// get VPN
VPN = (VirtualAddress & VPN_MASK) >> SHIFT// look up TLB
(Success, TlbEntry) = TLB_Lookup(VPN)if (Success == True)     // TLB Hit 
{if (CanAccess(TlbEntry.ProtectBits) == True) {Offset = VirtualAddress & OFFSET_MASKPhysAddr = (TlbEntry.PFN << SHIFT) | Offset    // get physical address from TLBAccessMemory(PhysAddr)                         // access physical memory}else {RaiseException(PROTECTION_FAULT) }
}
else                     // TLB Miss                  
{PTEAddr = PTBR + (VPN * sizeof(PTE))       // get PTE addressPTE = AccessMemory(PTEAddr)                // get PTEif (PTE.Valid == False) {RaiseException(SEGMENTATION_FAULT) }else if (CanAccess(PTE.ProtectBits) == False) {RaiseException(PROTECTION_FAULT) }else {TLB_Insert(VPN, PTE.PFN, PTE.ProtectBits)    // renew TLBRetryInstruction()                           // retry this instruction}
}

补充:TLB 未命中的情况,可能由硬件处理,也可能由软件(操作系统)处理。

假设有一个 8 位的虚拟地址空间,页帧大小为 16 字节,虚拟地址划分为 4 位的 VPN 和 4 位的 offset;

现在,我们要遍历一个整型数组 a[10],它在地址空间中的分布如下:

在这里插入图片描述

在访问元素 a[0]a[3]a[7] 时,TLB 会出现「未命中」的情况,整体的命中率为 70%。

对于典型的 4KB 大小的页来说,这种密集的数组访问会实现极好的 TLB 性能,每个页的访问只有一次「未命中」。

TLB 结构

典型的 TLB 有 32 项、64 项、128 项,并且是全相联的(fully associative)。

基本上,这就意味着一条地址映射可能存在 TLB 中的任意位置,硬件会并行地查找 TLB,找到期望的转换映射。

一条 TLB 项的结构:[ VPN | PFN | 其他位 ]

TLB 项中可能包括以下位:

  • 有效位(valid bit)

    表明该 TLB 项是不是有效的地址映射。

  • 保护位(protection bit)

    表明该页是否可以读取、写入、执行。

  • 脏位(dirty bit)

    表明该页被带入内存后是否被修改过。

  • 全局位(Global bit)

    表明该页是不是所有进程全局共享的。

  • 地址空间标识符(ASID)

    用于区分不同的地址空间,ASID 位的引入允许 TLB 维护多个地址空间的映射。

管理 TLB

首先,在进行上下文切换时,TLB 会面临一些问题。

假设进程 P1 在 TLB 中缓存了有效的地址映射:VPN 10 → PFN 100;

操作系统进行上下文切换,运行进程 P2,P2 也在 TLB 中缓存一条地址映射:VPN 10 → PFN 170;

那么,在上下文切换时,如何管理 TLB 的内容?

  1. **清空 TLB:**将所有项的有效位(valid)置为 0

这是可行的解决方案,进程不会读到错误的地址映射;

每次切换进程后,访问数据和页,都会触发 TLB 未命中;如果 OS 频繁切换进程,产生的开销很高。

  1. **添加 ASID:**区分不同的地址空间

如前面讲到的,ASID 使得 TLB 可以维护多个地址空间的映射。

在这里插入图片描述

还有一个问题:在向 TLB 添加新项时,应该替换哪个旧项?

  1. 最近最少使用

LRU 算法利用了内存引用流中的局部性,假定最近没有用过的项,可能是好的换出候选项。

  1. 随机策略

随机选择一项换出去,该策略不仅简单,并且可以避免一种极端情况:

程序循环访问 n+1 个页,但 TLB 只能存放 n 个页;该情况下,LRU 每次访问内存时都会触发 TLB 未命中。

多级页表

前面的讨论都是基于「线性页表」,但是*「线性页表」占用的内存很大* 。

假设一个 32 位地址空间,4KB 的页和一个 4B 的页表项;一个地址空间中大约有一百万个虚拟页面,乘以页表项的大小,则一个页表大小为 4MB。那么,一百个进程,就要占用数百兆的内存!

因此,要寻找新的技术来减轻这种沉重的负担,即:多级页表(multi-level page table)

基本思路

「多级页表」的思路如下:

  • 首先,将页表分成页大小的单元;

  • 通过 **页目录(page directory)**来追踪页表的页表项是否有效;

  • 如果在「页目录」中记录的页无效,则不为该页的页表分配内存。

以一个两级页表为例,**页目录项(Page Directory Entries,PDE)**中记录着一个 有效位页帧号 PFN

在这里插入图片描述

通过增加一个间接层「页目录」,我们可以将「页表的页单元」放在物理内存中的任何地方!

地址转换

现在,我们来构建一个二级页表。

假设有一个 16KB 的虚拟地址空间,页帧大小为 64 字节,虚拟地址划分为 8 位的 VPN 和 6 位的 offset;

那么,应该有 2 8 = 256 2^8=256 28=256个「页表项」,假设每个 PTE 的大小为 4 字节,则页表的大小为 1KB,占 16 个页帧。

我们将 VPN 的前 4 位划分为 页目录索引(PDIndex),剩余的位为 页表索引(PTIndex),如下所示:

在这里插入图片描述

  • PDEAddr = PageDirBase + (PDIndex * sizeof(PDE))

  • PTEAddr = (PDE.PFN << SHIFT) + (PTIndex * sizeof(PTE))

// get VPN
VPN = (VirtualAddress & VPN_MASK) >> SHIFT // look up TLB
(Success, TlbEntry) = TLB_Lookup(VPN) if (Success == True)   // TLB Hit 
{if (CanAccess(TlbEntry.ProtectBits) == True) {Offset = VirtualAddress & OFFSET_MASK PhysAddr = (TlbEntry.PFN << SHIFT) | Offset Register = AccessMemory(PhysAddr)            // access physical memory}else{RaiseException(PROTECTION_FAULT) }
}
else                   // TLB Miss 
{// first, get page directory entry PDIndex = (VPN & PD_MASK) >> PD_SHIFT PDEAddr = PDBR + (PDIndex * sizeof(PDE)) PDE = AccessMemory(PDEAddr)                      // get PDEif (PDE.Valid == False) {RaiseException(SEGMENTATION_FAULT) }else {// PDE is valid: now fetch PTE from page table PTIndex = (VPN & PT_MASK) >> PT_SHIFT PTEAddr = (PDE.PFN << SHIFT) + (PTIndex * sizeof(PTE)) PTE = AccessMemory(PTEAddr)                  // get PTEif (PTE.Valid == False) {RaiseException(SEGMENTATION_FAULT) }else if (CanAccess(PTE.ProtectBits) == False) {RaiseException(PROTECTION_FAULT) }else {TLB_Insert(VPN, PTE.PFN, PTE.ProtectBits) RetryInstruction()}}
}

时空折中

「多级页表」是有成本的。

当 TLB 未命中时,需要从内存加载两次(先访问页目录,后访问 PTE),才能从页表中获取正确的地址转换信息。

因此,「多级页表」是 时间—空间折中 的。

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

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

相关文章

python列表的循环遍历

数据容器&#xff1a;一个可以存储多个元素的Python数据类型 有哪些数据容器&#xff1a;list&#xff08;列表&#xff09;&#xff0c;tuple&#xff08;元组&#xff09;&#xff0c;str&#xff08;字符串&#xff09;&#xff0c;set&#xff08;集合&#xff09;&#x…

第三方电脑小爱同学用快捷键唤醒

第三方电脑安装小爱同学-CSDN博客 请结合之前安装小爱同学的教程安装过程请提前取消windows更新 安装完成之后登录账号即可使用 Ahk2.0 下载地址&#xff1a;https://www.autohotkey.com/download/ahk-v2.zip 打开链接即可自动下载&#xff0c;下载后解压出来点击install.cmd安…

微信公众服务号升级订阅号

服务号和订阅号有什么区别&#xff1f;服务号转为订阅号有哪些作用&#xff1f;首先我们要知道服务号和订阅号有什么区别。服务号侧重于对用户进行服务&#xff0c;每月可推送4次&#xff0c;每次最多8篇文章&#xff0c;发送的消息直接显示在好友列表中。订阅号更侧重于信息传…

java飞翔的鸟游戏

A.准备工作 Bird类 Column类 BirdGame类 Ground类 B.中间过程 准备工作&#xff1a; 安装Java开发环境&#xff08;JDK&#xff09;。选择一个集成开发环境&#xff08;IDE&#xff09;&#xff0c;如Eclipse、IntelliJ IDEA或NetBeans。 创建项目&#xff1a; 在IDE中创建一个…

数据结构和算法(全)

1.了解数据结构和算法 1.1 二分查找 二分查找&#xff08;Binary Search&#xff09;是一种在有序数组中查找特定元素的搜索算法。它的基本思想是将数组分成两半&#xff0c;然后比较目标值与中间元素的大小关系&#xff0c;从而确定应该在左半部分还是右半部分继续查找。这个…

一文了解java中volatile关键字

认识volatile volatile关键字的作用有两个&#xff1a;变量修改对其他线程立即可见、禁止指令重排。 第二个作用我们后面再讲&#xff0c;先主要讲一下第一个作用。通俗点来说&#xff0c;就是我在一个线程对一个变量进行了修改&#xff0c;那么其他线程马上就可以知道我修改…

树莓派zero w入坑指南

树莓派zero w入坑指南 入坑契机 说起创客不得不提到开源硬件Raspberry Pi(树莓派)。它是一款基于ARM的微型电脑主板&#xff0c;以MicroSD卡为硬盘&#xff0c;提供HDMI和USB等外部接口&#xff0c;可连接显示器和键鼠。以上部件全部整合在一张仅比信用卡稍大的主板上&#x…

pytorch_lightning 安装

在安装pytorch-lightning时一定注意自己的torch是pip安装还是conda安装&#xff0c;pytorch_lightning 安装方式要与torch的安装方式保持一致&#xff0c;否则也会导致你的torch版本被替换。 正确安装方式&#xff1a; pip方式&#xff1a; pip install pytorch-lightning版本…

issue unit

The Issue Unit issue queue用来hold住&#xff0c;已经dispatched&#xff0c;但是还没有执行的uops&#xff1b; 当一条uop的所有的operands已经ready之后&#xff0c;request请求会被拉起来&#xff1b;然后issue select logic将会从request bit 1的slot中&#xff0c;选择…

第十二章 React 路由配置,路由参数获取

一、专栏介绍 &#x1f436;&#x1f436; 欢迎加入本专栏&#xff01;本专栏将引领您快速上手React&#xff0c;让我们一起放弃放弃的念头&#xff0c;开始学习之旅吧&#xff01;我们将从搭建React项目开始&#xff0c;逐步深入讲解最核心的hooks&#xff0c;以及React路由、…

0基础学java-day19(IO流)

一、文件 1 什么是文件 2.文件流 3.常用的文件操作 3.1 创建文件对象相关构造器和方法 package com.hspedu.file;import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.Test;import java.io.File; import java.io.IOException;/*** author 林然* vers…

js根据数组对象中的某个值去重

原理&#xff1a;利用对象key-value进行去重 去重方法&#xff1a; // 数组对象根据某一个值去重 filterList(list[], key) {let obj {};list?.forEach(item>{obj[item[key]]item;});return Object.values(obj); }, 用法&#xff1a; let list [{id: 1, name: 1},{id…

【TI毫米波雷达入门-11】毫米波速度相关计算

知识回顾 傅里叶变换 信号用复数表示&#xff0c;A :振幅&#xff0c; Q &#xff1a;相位 中频 信号 中频信号的相位 中频信号的表达公式 频率和相位的表达方式 使用两个Chirp 实现单个目标的测量 两个连续的chirp &#xff0c;检测目标的相位差&#xff0c;通过速度和时间的关…

7+乳酸化+分型+实验,怎么贴合热点开展实验,这篇文章给你思路

今天给同学们分享一篇生信文章“Identification of lactylation related model to predict prognostic, tumor infiltrating immunocytes and response of immunotherapy in gastric cancer”&#xff0c;这篇文章发表在Front Immunol期刊上&#xff0c;影响因子为7.3。 结果解…

基于java的小型超市管理系统论文

摘 要 使用旧方法对超市信息进行系统化管理已经不再让人们信赖了&#xff0c;把现在的网络信息技术运用在超市信息的管理上面可以解决许多信息管理上面的难题&#xff0c;比如处理数据时间很长&#xff0c;数据存在错误不能及时纠正等问题。 这次开发的小型超市管理系统有管理…

uniapp 数组添加不重复元素

一、效果图 二、代码 //点击事件rightBtn(sub, index) {console.log(sub, index)//uniapp 数组添加不重复元素if (this.selectList.includes(sub.type)) {this.selectList this.selectList.filter((item) > {return item ! sub.type;});} else {this.selectList.push(sub.t…

365锦鲤助手 砍价小程序源码 流量主引流裂变

源码介绍 修改版365锦鲤 助手&#xff0c; 砍价小程序源码 流量主引流裂变 拼多多商品快速丰富产品内容满足广大用户需求&#xff1b;流量矩阵让流量都进你的圈子飞起来&#xff1b;长期盈利、项目稳定 1.后台安装微擎 2安装应用 后台打包上传

23.12.10日总结

周总结 这周三的晚自习&#xff0c;学姐讲了一下git的合作开发&#xff0c;还有懒加载&#xff0c;防抖&#xff0c;节流 答辩的时候问了几个问题&#xff1a; 为什么在js中0.10.2!0.3? 在js中进行属性运算时&#xff0c;会出现0.10.20.300000000000000004js遵循IEEE754标…

【有限元仿真】or【流体仿真】

流体和刚体的关系&#xff1f; 刚体仿真关注刚性物体的运动和力学行为。刚体是指在外力作用下保持形状和结构不变的物体&#xff0c;不受弯曲或拉伸的影响。刚体仿真基于刚体力学原理和刚体运动学方程&#xff0c;模拟刚体的运动、转动、碰撞等行为。它可以用于模拟刚体之间的…

Mysql进阶-InnoDB引擎事务原理及MVCC

事务原理 事务基础 事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向系 统提交或撤销操作请求&#xff0c;即这些操作要么同时成功&#xff0c;要么同时失败。 事务的四大特性&#xff1a; 原子性&#xff08;A…