为什么 Linux 需要虚拟内存(转载)

为什么 Linux 需要虚拟内存

原文链接: https://draveness.me/whys-the-design-os-virtual-memory/

操作系统中的 CPU 和主内存(Main memory)都是稀缺资源,所有运行在当前操作系统的进程会共享系统中的 CPU 和内存资源,操作系统会使用 CPU 调度器分配 CPU 时间1并引入虚拟内存系统以管理物理内存,本文会分析操作系统为什么需要虚拟内存。

在回答虚拟内存存在的必要性之前,我们需要理解操作系统中的虚拟内存是什么,它在操作系统中起到什么样的作用。正如软件工程中的其他抽象, 虚拟内存是操作系统物理内存和进程之间的中间层,它为进程隐藏了物理内存这一概念,为进程提供了更加简洁和易用的接口以及更加复杂的功能。

为什么 Linux 需要虚拟内存(转载)

图 1 – 进程和操作系统的中间层

如果需要我们从头设计一个操作系统,让系统中的进程直接访问主内存中的物理地址应该是非常自然的决定,早期的操作系统确实也都是这么实现的,进程会使用目标内存的物理地址(Physical Address)直接访问内存中的内容,然而现代的操作系统都引入了虚拟内存,进程持有的虚拟地址(Virtual Address)会经过内存管理单元(Memory Mangament Unit)的转换变成物理地址2,然后再通过物理地址访问内存:

为什么 Linux 需要虚拟内存(转载)

图 2 – 虚拟内存系统

主存储是相对比较稀缺的资源,虽然顺序读取只比磁盘快 1 个数量级,但是它能提供极快的随机访问速度,从内存上随机读取数据是磁盘的 100,000 倍3,充分利用内存的随机访问速度是改善程序执行效率的有效方式。

操作系统以页为单位管理内存,当进程发现需要访问的数据不在内存时,操作系统可能会将数据以页的方式加载到内存中,这个过程是由上图中的内存管理单元(MMU)完成的。操作系统的虚拟内存作为一个抽象层,起到了以下三个非常关键的作用:

  • 虚拟内存可以利用内存起到缓存的作用,提高进程访问磁盘的速度;
  • 虚拟内存可以为进程提供独立的内存空间,简化程序的链接、加载过程并通过动态库共享内存;
  • 虚拟内存可以控制进程对物理内存的访问,隔离不同进程的访问权限,提高系统的安全性;

缓存

我们可以将虚拟内存看作是在磁盘上一片空间,当这片空间中的一部分访问比较频繁时, 该部分数据会以页为单位被缓存到主存中以加速 CPU 访问数据的性能,虚拟内存利用空间较大的磁盘存储作为『内存』并使用主存储缓存进行加速, 让上层认为操作系统的内存很大而且很快,然而区域很大的磁盘并不快,而很快的内存也并不大

为什么 Linux 需要虚拟内存(转载)

图 3 – 虚拟内存、主存和磁盘

虚拟内存中的虚拟页(Virtual Page,VP)可能处于以下的三种状态 — 未分配(Unallocated)、未缓存(Uncached)和已缓存(Cached),其中未分配的内存页是没有被进程申请使用的,也就是空闲的虚拟内存,不占用虚拟内存磁盘的任何空间,未缓存和已缓存的内存页分别表示仅加载到磁盘中的内存页和已经加载到主存中的内存页。如上图所示,图中绿色的虚拟内存页由主存中的物理内存页(Physical Page,PP)支撑,所以它是已经缓存过的,而黄色的虚拟内存页仅在磁盘中,所以没有被物理内存缓存。

当用户程序访问未被缓存的虚拟页时,硬件就会触发缺页中断(Page Fault,PF),在部分情况下,被访问的页面已经加载到了物理内存中,但是用户程序的页表(Page Table)并不存在该对应关系,这时我们只需要在页表中建立虚拟内存到物理内存的关系;在其他情况下,操作系统需要将磁盘上未被缓存的虚拟页加载到物理内存中4

为什么 Linux 需要虚拟内存(转载)

图 4 – 虚拟内存的缺页中断

因为主内存的空间是有限的,当主内存中不包含可以使用的空间时,操作系统会从选择合适的物理内存页驱逐回磁盘,为新的内存页让出位置,选择待驱逐页的过程在操作系统中叫做页面替换(Page Replacement)。缺页中断和页面替换技术都是操作系统调页算法(Paging)的一部分,该算法的目的就是充分利用内存资源作为磁盘的缓存以提高程序的运行效率。

内存管理

虚拟内存可以为正在运行的进程提供独立的内存空间,制造一种每个进程的内存都是独立的假象,在 64 位的操作系统上,每个进程都会拥有 256 TiB 的内存空间,内核空间和用户空间分别占 128 TiB 5,部分操作系统使用 57 位虚拟地址以提供 128 PiB 的寻址空间6。因为每个进程的虚拟内存空间是完全独立的,所以它们都可以完整的使用 0x0000000000000000 到 0x00007FFFFFFFFFFF 的全部内存。

为什么 Linux 需要虚拟内存(转载)

图 5 – 操作系统的虚拟内存空间

虚拟内存空间只是操作系统中的逻辑结构,就像我们上面说的,应用程序最终还是需要访问物理内存或者磁盘上的内容。因为操作系统加了一个虚拟内存的中间层,所以我们也需要为进程实现地址翻译器,实现从虚拟地址到物理地址的转换,页表是虚拟内存系统中的重要数据结构,每一个进程的页表中都存储了从虚拟内存到物理内存页的映射关系,为了存储 64 位操作系统中 128 TiB 虚拟内存的映射数据,Linux 在 2.6.10 中引入了四层的页表辅助虚拟地址的转换7,在 4.11 中引入了五层的页表结构8,在未来还可能会引入更多层的页表结构以支持 64 位的虚拟地址。

为什么 Linux 需要虚拟内存(转载)

图 6 – 四层页表结构

在如上图所示的四层页表结构中,操作系统会使用最低的 12 位作为页面的偏移量,剩下的 36 位会分四组分别表示当前层级在上一层中的索引,所有的虚拟地址都可以用上述的多层页表查找到对应的物理地址。

因为有多层的页表结构可以用来转换虚拟地址,所以多个进程可以通过虚拟内存共享物理内存。我们在 为什么 Redis 快照使用子进程 一文中介绍的写时复制就利用了虚拟内存的这个特性,当我们在 Linux 中调用 fork 创建子进程时,实际上 只复制了父进程的页表。如下图所示,父子进程会通过不同的页表指向相同的物理内存:

为什么 Linux 需要虚拟内存(转载)

图 7 – 进程间共享内存

虚拟内存不仅可以在 fork 时用于共享进程的物理内存,提供写时复制的机制,还能共享一些常见的动态库减少物理内存的占用,所有的进程都可能调用相同的操作系统内核代码,而 C 语言程序也会调用相同的标准库。

除了能够共享内存之外,独立的虚拟内存空间也会简化内存的分配过程,当用户程序向操作系统申请堆内存时,操作系统可以分配几个连续的虚拟页,但是这些虚拟页可以对应到物理内存中不连续的页中。

内存保护

操作系统中的用户程序不应该修改只读的代码段,也不应该读取或者修改内核中的代码和数据结构或者访问私有的以及其他的进程的内存,如果无法对用户进程的内存访问进行限制,攻击者就可以访问和修改其他进程的内存影响系统的安全。

如果每一个进程都持有独立的虚拟内存空间,那么虚拟内存中页表可以理解成进程和物理页的『连接表』,其中可以存储进程和物理页之间的访问关系,包括读权限、写权限和执行权限:

为什么 Linux 需要虚拟内存(转载)

图 8 – 读权限、写权限和执行权限

内存管理单元可以决定当前进程是否有权限访问目标的物理内存,这样我们就最终将权限管理的功能全部收敛到虚拟内存系统中,减少了可能出现风险的代码路径。

总结

虚拟内存的设计方法可以说是软件工程中的常见手段,通过结合磁盘和内存各自的优势, 利用中间层对资源进行更合理地调度充分提高资源的利用率并提供和谐以及统一的抽象,而在实际的业务场景中,类似的缓存逻辑也比较常见。

操作系统的虚拟内存是非常复杂的组件,没有工程师能够了解其中的全部细节,不过了解虚拟内存的整体设计也很有价值,我们能够从中找到很多软件设计的方法。我们重新回到今天的问题 — Linux 操作系统中为什么需要虚拟内存:

  • 虚拟内存可以结合磁盘和物理内存的优势为进程提供看起来速度足够快并且容量足够大的存储;
  • 虚拟内存可以为进程提供独立的内存空间并引入多层的页表结构将虚拟内存翻译成物理内存,进程之间可以共享物理内存减少开销,也能简化程序的链接、装载以及内存分配过程;
  • 虚拟内存可以控制进程对物理内存的访问,隔离不同进程的访问权限,提高系统的安全性;

到最后,我们还是来看一些比较开放的相关问题,有兴趣的读者可以仔细思考一下下面的问题:

  • 为什么每层的页表结构只能够负责 9 位虚拟地址的寻址?
  • 64 位的虚拟内存在操作系统中需要多少层的页表结构才能寻址?

Original: https://www.cnblogs.com/wuxianfeng023/p/16698809.html
Author: 松勤吴老师
Title: 为什么 Linux 需要虚拟内存(转载)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/603045/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 数据分析(2)Matplotlib绘制2D图表

    文章目录 * – 一. 线型图 – + (一)绘制单条线型图 + (二)绘制多条线型图 + (三)绘制折线图 – 二. 直方图与密度图 &#82…

    Python 2023年9月4日
    058
  • Numpy+Pandas+Matplotlib学习

    参考的教程是哔哩哔哩孙兴华UP主的视频,边学边做 BV1ji4y157uB 代码及所用的数据已上传至Githublearn_numpy_pandas_matplotlib 文章目录…

    Python 2023年8月31日
    032
  • 2022年5种主流的Python框架推荐!

    推荐文章 很多小伙伴都发现了,用户自主「申请上首页」的按钮取消了,那博主们写的文章还有上首页曝光的机会吗?我们的回答是”当然有!!!”虽然我们取消了上首页申…

    Python 2023年5月24日
    071
  • VSCode远程连接服务器显示图像

    主要是为了在远程服务器的python中,使用matplotlib和opencv的imshow显示图像,配置好后也可以使用远程服务器的其它图形化界面,gedit之类的。 以下文字内 …

    Python 2023年8月31日
    0287
  • 使用matplotlib和opencv将彩色图转换为灰度图

    1 读取图像 API cv.imread() 参数: 读取方式的标志 cv.IMREAD*COLOR:以彩色模式加载图像,任何图像的透明度都将被忽略。这是默认参数。 cv.IMRE…

    Python 2023年9月3日
    043
  • day31-线程基础01

    线程基础01 1.程序 进程 线程 程序(program):是为完成的特定任务,用某种语言编写的一组指令的集合。简单来说,就是我们写的代码。 进程: 进程是指运行中的程序,比如我们…

    Python 2023年10月23日
    026
  • 【2022-09-29】DRF从入门到入土(四)

    drf请求与响应 Request REST framework 传入视图的request对象不再是Django默认的HttpRequest对象,而是REST framework提供…

    Python 2023年6月9日
    070
  • matplot 坐标点_matplotlib 对坐标的控制,加图例注释的操作

    我们可以使用matplotlib.pyplot.locator_params()来控制刻度线定位器的行为。 即使通常会自动确定标记点的位置,但是如果我们想要绘制较小的标记,则可以控…

    Python 2023年9月6日
    053
  • 强化学习——多智能体强化学习

    文章目录 前言 多智能体系统的设定 合作关系设定下的多智能体系统 * 策略学习的目标函数 合作关系下的多智能体策略学习算法MAC-A2C 非合作关系下的多智能体策略学习算法MAC-…

    Python 2023年9月16日
    045
  • 【机器学习笔记】吴恩达机器学习,带你入门人工智能的世界

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

    Python 2023年9月27日
    057
  • pygame2 画点线

    一、复习: 首先将上次画的矩形做复杂一些的小程序: import pygame,sys, random pygame.init() screen = pygame.display….

    Python 2023年9月18日
    034
  • 1813. 句子相似性 III

    中等91 相关企业 一个句子是由一些单词与它们之间的单个空格组成,且句子的开头和结尾没有多余空格。比方说,”Hello World”,”HELL…

    Python 2023年11月6日
    033
  • CentOS7-之Superset安装

    1 准备工作,安装python环境 在不破坏linux原先的python2.7的情况下安装python3.7 这是一个开源的python的版本管理工具,与anaconda差不大,只…

    Python 2023年8月15日
    033
  • 容器镜像多架构支持介绍

    容器镜像多架构支持介绍 简介 出于开发需要,我们经常会需要浏览公共镜像库,以选取合适的基础镜像,在浏览过程中,不经意地会发现部分镜像的一个tag下列出了许多种架构,如下图所示,de…

    Python 2023年10月15日
    052
  • 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵

    💡 作者:韩信子@ShowMeAI📘 数据分析实战系列:https://www.showmeai.tech/tutorials/40📘 本文地址:https://www.showm…

    Python 2023年10月28日
    045
  • python之元组基础相关

    创建元组:只需要在括号中添加元素,并使用逗号隔开即可 tup1 = (‘Google’, ‘Runoob’, 1997, 2000) tup2 = (1, 2, 3, 4, 5) …

    Python 2023年6月3日
    059
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球