首页 > 系统相关 >虚拟内存知识详解

虚拟内存知识详解

时间:2024-04-06 09:58:24浏览次数:23  
标签:分段 知识 虚拟地址 物理地址 地址 详解 页表 虚拟内存 内存

  • 虚拟内存

    • 单片机的 CPU 是直接操作内存的「物理地址」

    • 在这种情况下,要想在内存中同时运行两个程序是不可能的

    • 操作系统是如何解决这个问题呢?

      • 关键的问题是这两个程序都引用了绝对物理地址,而这正是我们最需要避免的。

      • 可以把进程所使用的地址「隔离」开来,即让操作系统为每个进程分配独立的一套「虚拟地址」

      • 人人都有,大家自己玩自己的地址就行,互不干涉

      • 前提每个进程都不能访问物理地址,至于虚拟地址最终怎么落到物理内存里,对进程来说是透明的,操作系统已经把这些都安排的明明白白了。

      • 操作系统会提供一种机制,将不同进程的虚拟地址和不同内存的物理地址映射起来。

      • 如果程序要访问虚拟地址的时候,由操作系统转换成不同的物理地址,这样不同的进程运行的时候,写入的是不同的物理地址,这样就不会冲突了

        • 我们程序所使用的内存地址叫做虚拟内存地址(Virtual Memory Address

        • 实际存在硬件里面的空间地址叫物理内存地址(Physical Memory Address)。

      • 进程持有的虚拟地址会通过 CPU 芯片中的内存管理单元(MMU)的映射关系,来转换变成物理地址,然后再通过物理地址访问内存

    • 操作系统是如何管理虚拟地址与物理地址之间的关系?

      • 主要有两种方式,分别是内存分段内存分页

  • 内存分段

    • 程序是由若干个逻辑分段组成的,如可由代码分段、数据分段、栈段、堆段组成。

    • 不同的段是有不同的属性的,所以就用分段(Segmentation)的形式把这些段分离出来。

    • 分段机制下,虚拟地址和物理地址是如何映射的?

      • 虚拟地址由两部分组成,段选择因子和段内偏移量。

        • 段选择子就保存在段寄存器里面。段选择子里面最重要的是段号,用作段表的索引。段表里面保存的是这个段的基地址、段的界限和特权等级等。

        • 虚拟地址中的段内偏移量应该位于 0 和段界限之间,如果段内偏移量是合法的,就将段基地址加上段内偏移量得到物理内存地址

      • 知道了虚拟地址是通过段表与物理地址进行映射的,分段机制会把程序的虚拟地址分成 4 个段,每个段在段表中有一个项,在这一项找到段的基地址,再加上偏移量,于是就能找到物理内存中的地址

      • 分段的办法很好,解决了程序本身不需要关心具体的物理内存地址的问题,但它也有一些不足之处

        • 第一个就是内存碎片的问题。

        • 第二个就是内存交换的效率低的问题。

        • 内存分段会出现内存碎片吗?

          • 内部内存碎片外部内存碎片

          • 内存分段管理可以做到段根据实际需求分配内存,所以有多少需求就分配多大的段,所以不会出现内部内存碎片。

          • 但是由于每个段的长度不固定,所以多个段未必能恰好使用所有的内存空间,会产生了多个不连续的小物理内存,导致新的程序无法被装载,所以会出现外部内存碎片的问题。

          • 解决「外部内存碎片」的问题就是内存交换

            • 先写到硬盘上再重新写回内存,这样可以把内存的位置集中,去掉碎片

        • 分段为什么会导致内存交换效率低的问题?

          • 对于多进程的系统来说,用分段的方式,外部内存碎片是很容易产生的,产生了外部内存碎片,那不得不重新 Swap 内存区域,这个过程会产生性能瓶颈。

          • 因为硬盘的访问速度要比内存慢太多了,每一次内存交换,我们都需要把一大段连续的内存数据写到硬盘上。

          • 如果内存交换的时候,交换的是一个占内存空间很大的程序,这样整个机器都会显得卡顿。

  • 内存分页

    • 分段的好处就是能产生连续的内存空间,但是会出现「外部内存碎片和内存交换的空间太大」的问题。

    • 当需要进行内存交换的时候,让需要交换写入或者从磁盘装载的数据更少一点,这样就可以解决问题了。这个办法,也就是内存分页(Paging)。

    • 分页是把整个虚拟和物理内存空间切成一段段固定尺寸的大小。

    • 这样一个连续并且尺寸固定的内存空间,我们叫页(Page)。在 Linux 下,每一页的大小为 4KB

    • 页表是存储在内存里的,内存管理单元 (MMU)就做将虚拟内存地址转换成物理地址的工作。

    • 当进程访问的虚拟地址在页表中查不到时,系统会产生一个缺页异常,进入系统内核空间分配物理内存、更新进程页表,最后再返回用户空间,恢复进程的运行。

    • 分页是怎么解决分段的「外部内存碎片和内存交换效率低」的问题?

      • 采用了分页,页与页之间是紧密排列的,所以不会有外部碎片

      • 内存分页机制会有内部内存碎片

      • 如果内存空间不够,操作系统会把其他正在运行的进程中的「最近没被使用」的内存页面给释放掉,也就是暂时写在硬盘上,称为换出(Swap Out

      • 一旦需要的时候,再加载进来,称为换入

      • 一次性写入磁盘的也只有少数的一个页或者几个页,不会花太多时间,内存交换的效率就相对比较高。

      • 只有在程序运行中,需要用到对应虚拟内存页里面的指令和数据时,再加载到物理内存里面去。

    • 分页机制下,虚拟地址和物理地址是如何映射的?

      • 在分页机制下,虚拟地址分为两部分,页号和页内偏移。

      • 页号作为页表的索引,页表包含物理页每页所在物理内存的基地址,这个基地址与页内偏移的组合就形成了物理内存地址

      • 对于一个内存地址转换,其实就是这样三个步骤

        • 把虚拟内存地址,切分成页号和偏移量;

        • 根据页号,从页表里面,查询对应的物理页号;

        • 直接拿物理页号,加上前面的偏移量,就得到了物理内存地址。

    • 简单的分页有什么缺陷吗?

      • 有空间上的缺陷。

      • 因为操作系统是可以同时运行非常多的进程的,那这不就意味着页表会非常的庞大。

    • 多级页表

      • 要解决上面的问题,就需要采用一种叫作多级页表(Multi-Level Page Table)的解决方案。

      • 对于单页表的实现方式,在 32 位和页大小 4KB 的环境下,一个进程的页表需要装下 100 多万个「页表项」,并且每个页表项是占用 4 字节大小的,于是相当于每个页表需占用 4MB 大小的空间。

      • 把这个 100 多万个「页表项」的单级页表再分页,将页表(一级页表)分为 1024 个页表(二级页表),每个表(二级页表)中包含 1024 个「页表项」,形成二级分页。

        • 分了二级表,映射 4GB 地址空间就需要 4KB(一级页表)+ 4MB(二级页表)的内存,这样占用空间不是更大了吗?

          • 我们应该换个角度来看问题,还记得计算机组成原理里面无处不在的局部性原理么?

          • 如果使用了二级分页,一级页表就可以覆盖整个 4GB 虚拟地址空间,但如果某个一级页表的页表项没有被用到,也就不需要创建这个页表项对应的二级页表了,即可以在需要时才创建二级页表

          • 那么为什么不分级的页表就做不到这样节约内存呢?

            • 页表一定要覆盖全部虚拟地址空间,不分级的页表就需要有 100 多万个页表项来映射,而二级分页则只需要 1024 个页表项(此时一级页表覆盖到了全部虚拟地址空间,二级页表在需要时创建)。

          • 对于 64 位的系统,两级分页肯定不够了,就变成了四级目录,分别是:

            • 全局页目录项 PGD(Page Global Directory);

            • 上层页目录项 PUD(Page Upper Directory);

            • 中间页目录项 PMD(Page Middle Directory);

            • 页表项 PTE(Page Table Entry);

    • TLB

      • 多级页表虽然解决了空间上的问题,但是虚拟地址到物理地址的转换就多了几道转换的工序,这显然就降低了这俩地址转换的速度,也就是带来了时间上的开销。

      • 程序是有局部性的

      • 利用这一特性,把最常访问的几个页表项存储到访问速度更快的硬件。在 CPU 芯片中,加入了一个专门存放程序最常访问的页表项的 Cache,这个 Cache 就是 TLB(Translation Lookaside Buffer) ,通常称为页表缓存、转址旁路缓存、快表等。

        • 在 CPU 芯片里面,封装了内存管理单元(Memory Management Unit)芯片,它用来完成地址转换和 TLB 的访问与交互。

        • 有了 TLB 后,那么 CPU 在寻址时,会先查 TLB,如果没找到,才会继续查常规的页表。

  • 段页式内存管理

    • 内存分段和内存分页并不是对立的,它们是可以组合起来在同一个系统中使用的,那么组合起来后,通常称为段页式内存管理。

    • 段页式内存管理实现的方式:

      • 先将程序划分为多个有逻辑意义的段,也就是前面提到的分段机制;

      • 接着再把每个段划分为多个页,也就是对分段划分出来的连续空间,再划分固定大小的页

    • 地址结构就由段号、段内页号和页内位移三部分组成。

    • 用于段页式地址变换的数据结构是每一个程序一张段表,每个段又建立一张页表,段表中的地址是页表的起始地址,而页表中的地址则为某页的物理页号

    • 段页式地址变换中要得到物理地址须经过三次内存访问

      • 第一次访问段表,得到页表起始地址;

      • 第二次访问页表,得到物理页号;

      • 第三次将物理页号与页内位移组合,得到物理地址。

    • 增加了硬件成本和系统开销,但提高了内存的利用率

  • Linux内存布局

    • 页式内存管理的作用是在由段式内存管理所映射而成的地址上再加上一层地址映射。

    • 逻辑地址和线性地址

      • 程序所使用的地址,通常是没被段式内存管理映射的地址,称为逻辑地址;

      • 通过段式内存管理映射的地址,称为线性地址,也叫虚拟地址;

      • 逻辑地址是「段式内存管理」转换前的地址,线性地址则是「页式内存管理」转换前的地址。

    • Linux 内存主要采用的是页式内存管理,但同时也不可避免地涉及了段机制。

    • Linux 内核所采取的办法是使段式映射的过程实际上不起什么作用

    • Linux 系统中的每个段都是从 0 地址开始的整个 4GB 虚拟空间(32 位环境下),也就是所有的段的起始地址都是一样的。这意味着,Linux 系统中的代码,包括操作系统本身的代码和应用程序代码,所面对的地址空间都是线性地址空间(虚拟地址),这种做法相当于屏蔽了处理器中的逻辑地址概念,段只被用于访问控制和内存保护。

    • Linux 的虚拟地址空间是如何分布的?

      • 虚拟地址空间的内部又被分为内核空间和用户空间两部分

        • 32 位系统的内核空间占用 1G,位于最高处,剩下的 3G 是用户空间;

        • 64 位系统的内核空间和用户空间都是 128T,分别占据整个内存空间的最高和最低处,剩下的中间部分是未定义的。

      • 内核空间与用户空间的区别

        • 进程在用户态时,只能访问用户空间内存;

        • 只有进入内核态后,才可以访问内核空间的内存;

      • 每个虚拟内存中的内核地址,其实关联的都是相同的物理内存这样,进程切换到内核态后,就可以很方便地访问内核空间内存。

      • 用户空间分布

        • 代码段,包括二进制可执行代码;

        • 数据段,包括已初始化的静态常量和全局变量;

        • BSS 段,包括未初始化的静态变量和全局变量;

        • 堆段,包括动态分配的内存,从低地址开始向上增长;

        • 文件映射段,包括动态库、共享内存等,从低地址开始向上增长;

        • 栈段,包括局部变量和函数调用的上下文等。栈的大小是固定的,一般是 8 MB。当然系统也提供了参数,以便我们自定义大小;

标签:分段,知识,虚拟地址,物理地址,地址,详解,页表,虚拟内存,内存
From: https://blog.csdn.net/m0_63310537/article/details/137420913

相关文章

  • 详解 Redis 在 Ubuntu 系统上的安装
    在Ubuntu20.04安装Redis1.先切换到root用户在Ubuntu20.04中,可以通过以下步骤切换到root用户:输入以下命令,以root用户身份登录:sudosu-按回车键,并输入当前用户的密码(即具有sudo权限的用户的密码)如果密码正确,将会切换到root用户,并且提示符会变为以r......
  • Redis各个方面入门详解
    目录一、Redis介绍二、分布式缓存常见的技术选型方案三、Redis和Memcached的区别和共同点四、缓存数据的处理流程五、Redis作为缓存的好处六、Redis常见数据结构以及使用场景七、Redis单线程模型八、Redis给缓存数据设置过期时间九、Redis判断数据过期的原理十......
  • 【c语言】自定义类型:联合体(公用体)【详解】
    联合体联合体类型的声明像结构体⼀样,联合体也是由⼀个或者多个成员构成,这些成员可以不同的类型。但是编译器只为最⼤的成员分配⾜够的内存空间。联合体的特点是所有成员共⽤同⼀块内存空间。所以联合体也叫:共用体。给联合体其中⼀个成员赋值,其他成员的值也跟着变化。......
  • Linux 用户、用户组 useradd、groupadd等详解
    ......
  • 向量数据库+知识图谱构建高效 RAG 系统
    向量数据库+知识图谱构建高效RAG系统数据的智能化存储向量数据库__RAG中的向量数据库知识图谱__构建智能化的知识体系向量数据库+知识图谱构建高效RAG第一、数据预处理和嵌入第二、检索和信息提取第三、生成和优化第四、多模态融合数据的智能化存储向量数据库是......
  • 微信小程序开发 基础知识(持续更新中~)
    ......
  • Autosar-BswM 配置详解(免费)-2
    上表配置内容的意思是,BswM初始化过程中会先走到“BswM_AR_InitBlockTwo”这个步骤,如果条件满足的话,会执行这个步骤所有的动作。执行完后“BswM_AR_InitBlockTwo”,会切换到“BswM_AR_ReadAllComplete”这个步骤,如果条件满足的话,执行完该步骤的所有动作。之后再次切换到run的......
  • [C++][C++11][智能指针]分析详解 + 代码模拟
    目录0.智能指针三要素:)1.为什么需要智能指针?2.内存泄漏1.什么是内存泄漏?内存泄漏的危害?2.内存泄漏分类(了解)3.如何检测内存泄漏4.如何避免内存泄漏3.RAII4.智能指针原理5.auto_ptr(失败设计)6.unique_ptr7.shared_ptr1.实现原理:通过引用计数的方式来实现多个shared_ptr......
  • 数据结构:详解【树和二叉树】
    1.树的概念及结构(了解)1.1树的概念树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。1.2树的结构1.3树与非树:1.4树在实际中的运用(表示文件系统的目录树结构)2.......
  • 最简单知识点PyTorch中的nn.Linear(1, 1)
    一、nn.Linear(1,1)nn.Linear(1,1) 是PyTorch中的一个线性层(全连接层)的定义。nn 是PyTorch的神经网络模块(torch.nn)的常用缩写。nn.Linear(1,1) 的含义如下:第一个参数 1:输入特征的数量。这表示该层接受一个长度为1的向量作为输入。第二个参数 1:输出特征的数量......