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

news2024/11/15 5:16:27

引入

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

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

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

  • 将物理内存看成是定长槽块的阵列,叫作 页帧 (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_MASK
        PhysAddr = (TlbEntry.PFN << SHIFT) | Offset    // get physical address from TLB
        AccessMemory(PhysAddr)                         // access physical memory
    }
    else 
    {
        RaiseException(PROTECTION_FAULT) 
    }
}
else                     // TLB Miss                  
{
    PTEAddr = PTBR + (VPN * sizeof(PTE))       // get PTE address
    PTE = AccessMemory(PTEAddr)                // get PTE
    if (PTE.Valid == False) 
    {
        RaiseException(SEGMENTATION_FAULT) 
    }
    else if (CanAccess(PTE.ProtectBits) == False) 
    {
        RaiseException(PROTECTION_FAULT) 
    }
    else 
    {
        TLB_Insert(VPN, PTE.PFN, PTE.ProtectBits)    // renew TLB
        RetryInstruction()                           // 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 PDE
    if (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 PTE
        if (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.coloradmin.cn/o/1310657.html

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

相关文章

斑马zebra目标检测数据集VOC+YOLO格式2300张

斑马是由四百万年前的原马进化出来的&#xff0c;最早出现的斑马可能是细纹斑马。有关史前马科动物的化石现存于美国爱达荷州克文的克文化石床国家博物馆。斑马的史前马为“克文马”&#xff08;美洲斑马或者克文斑马&#xff09;&#xff0c;学名为“Equussimplicidens”&…

智能守护,数据安全稳中求胜!上海迅软DSE助力家具家电行业引领潮流!

随着中国经济的蓬勃发展&#xff0c;家具家电企业正迎来“精品制造”的时代&#xff0c;业内竞争日益激烈。为了提升产品竞争力、扩大市场占有率&#xff0c;企业亟需加强对自主品牌的安全建设&#xff0c;确保品牌的自主知识产权、产品生产资料以及销售信息等核心数据不受泄漏…

Docker真的好难用啊,为什么说它移植性好啊?

看起来你对Docker有点困惑和挑战呀。Docker刚开始确实有点难以入门&#xff0c;但是一旦掌握了它的核心概念和操作&#xff0c;你会发现它其实非常强大和便利。 接下来我会根据你提出的问题和场景&#xff0c;详细地解答。 关于你的实际问题&#xff1a; 刚接触时的困难是正。…

如何实现服务注册与发现?

本文主要讲解如何实现服务注册与发现。 在分布式服务中&#xff0c;服务注册和发现是一个特别重要的概念&#xff0c;为什么需要服务注册和发现&#xff1f;常用的服务发现组件有哪些&#xff1f;服务注册和发现对一致性有哪些要求呢?下面我们就来学习服务发现相关的知识。 …

【五】Python 代理模式

文章目录 5.1 代理模式概述5.1.1 代理介绍5.1.2 代理模式的作用 5.2 代理模式的UML类图5.3 了解不同类型的代理5.3.1虚拟代理5.3.2 远程代理5.3.3 保护代理5.3.4 智能代理 5.4 现实世界中的代理模式5.5 代理模式的优点5.6 门面模式和代理模式之间的比较 5.1 代理模式概述 5.1.…

用XAMPP在Windows系统构建一个本地Web服务器

用XAMPP在Windows系统构建一个本地Web服务器 Build a Local Web Server for Windows with XAMPP By JacksonML 本文简要介绍如何获取和安装XAMPP以实现Windows环境下本地Web服务器的过程&#xff0c;希望对广大网友和学生有所帮助。 所谓本地Web服务器&#xff0c;即使用本地…

Python框架篇(5):FastApi-中间件使用

1.介绍 1.1 官网介绍 "中间件"是一个函数,它在每个请求被特定的路径操作处理之前,以及在每个响应返回之前工作. 它接收你的应用程序的每一个 请求. 然后它可以对这个 请求做一些事情或者执行任何需要的代码. 然后它将 请求传递给应用程序的其他部分 (通过某种 路径操…

slurm 23.11.0集群 debian 11.5 安装

slurm 23.11.0集群 debian 11.5 安装 用途 Slurm(Simple Linux Utility for Resource Management&#xff0c; http://slurm.schedmd.com/ )是开源的、具有容错性和高度可扩展的Linux集群超级计算系统资源管理和作业调度系统。超级计算系统可利用Slurm对资源和作业进行管理&a…

变电站蓄电池在线监测系统(论文+源码)

1. 系统设计 本次课题为变电站蓄电池在线监测系统的设计&#xff0c;其系统架构如图3.1所示&#xff0c;包括了主控制器STC89C52单片机&#xff0c;液晶显示器LCD1602,模数转换器ADC0832&#xff0c;电流传感器ACS712&#xff0c;分压电阻&#xff0c;蜂鸣器以及温度传感器。在…

Amazon SageMaker: 拓展机器学习边界,塑造未来创新趋势

授权说明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 亚马逊云科技开发者社区, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道。 近期在 re:Invent 2023 大会上&#xff0c;亚马逊云科技发布了一…

如何将Galaxybase图数据库应用于电力设备管理

导读 近日&#xff0c;受强冷空气影响&#xff0c;部分北方地区出现不同程度的降雪&#xff0c;并持续降温。据国家电网发布的预警通知&#xff0c;要求启动预警响应和应急机制&#xff0c;密切跟踪灾害预警信息和应急响应情况&#xff0c;滚动研判分析覆冰、积雪、低温等对电…

vite+vue3+electron搭建项目

编辑器使用vscode&#xff0c;打开一个空文件夹 第一步 初始化vite项目 初始化vite项目&#xff0c;命令 npm init vite 第二步 下载依赖 进入新建的项目&#xff0c;下载依赖&#xff0c;命令 cd vite-projec npm i第三步 使用cnpm下载 electron依赖 新建一个终端&#…

雪花算法详细讲解

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…

HNCTF

[Week1]Interesting_http 题目提示&#xff1a;Give me your want! POST方式传参want参数&#xff0c;先随便传want1&#xff1b; 题目问你想要什么&#xff0c;肯定是flag呗&#xff0c;传参wantflag&#xff1b;提示不是admin 将数据包中的Cookie&#xff1a;usernotadmin修…

C# 实现图片的压缩和改变大小png、jpg和gif

环境 .net6 Magick.NET-Q16-AnyCPU 13.5 Magick.NET源码 代码 using ImageMagick;namespace ImageCompress {internal class Program{static void Main(string[] args){string inputPath "imgloading.gif"; // 输入的GIF文件路径 string outputPath "im…

ChatGPT Plus重新开启订阅

12月14日凌晨&#xff0c;OpenAI首席执行官Sam Altman在社交平台宣布&#xff0c;终于找到了更多的GPU算力&#xff0c;重新开启订阅ChatGPT Plus。 上个月15日&#xff0c;OpenAI就因为算力不足&#xff0c;以及用户激增等原因暂停了ChatGPT Plus订阅。 Sam表示&#xff0c;在…

nginx的location与rewrite

目录 一.location 二.rewrite rewrite跳转实现&#xff1a; 语法格式&#xff1a;rewrite [flag]; flag标记说明&#xff1a; 三.基于域名跳转 四.基于ip跳转 五.基于旧域名跳转到新域名后面加目录 六.基于参数匹配的跳转 可以同过全局变量来匹配&#xff1a; 基于目…

智能分析/可视化安防监控系统EasyCVR风光互补远程视频监控方案

一、背景需求 在一些偏远地区&#xff0c;也具有视频监控的需求。但是这类场景中&#xff0c;一般无法就近获取市电&#xff0c;如果要长距离拉取市电&#xff0c;建设的成本非常高且长距离传输有安全隐患&#xff0c;因此风光互补远程视频监控方案的需求也较多。利用风光电转…

【数组Array】力扣-303 区域和检索 - 数组不可变

目录 题目描述 解题过程 labuladong题解 题目描述 给定一个整数数组 nums&#xff0c;处理以下类型的多个查询: 计算索引 left 和 right &#xff08;包含 left 和 right&#xff09;之间的 nums 元素的 和 &#xff0c;其中 left < right 实现 NumArray 类&#xff…

【送书活动五期】Go语言开发规范指南

今天和一个小伙伴偶尔聊了两句&#xff0c;聊到现在工作的开发语言&#xff0c;大学时接触的第一个语言应该是html&#xff0c;系统且简单的学习了前端语言&#xff0c;之后伴随着学校的课程&#xff0c;C、C#、Java都有涉及&#xff0c;然后就一直已Java为主了&#xff0c;也是…