操作系统内功篇:内存管理之虚拟内存

一 虚拟内存

在这种情况下,要想在内存中同时运行两个程序是不可能的。如果第一个程序在 2000 的位置写入一个新的值,将会擦掉第二个程序存放在相同位置上的所有内容,所以同时运行两个程序是根本行不通的,这两个程序会立刻崩溃。

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

这里关键的问题是这两个程序都引用了绝对物理地址,而这正是我们最需要避免的。 我们可以把进程所使用的地址「隔离」开来,即让操作系统为每个进程分配独立的一套「虚拟地址」,人人都有,大家自己玩自己的地址就行,互不干涉。但是有个前提每个进程都不能访问物理地址,至于虚拟地址最终怎么落到物理内存里,对进程来说是透明的,操作系统已经把这些都安排的明明白白了。 操作系统会提供一种机制,将不同进程的虚拟地址和不同内存的物理地址映射起来。 如果程序要访问虚拟地址的时候,由操作系统转换成不同的物理地址,这样不同的进程运行的时候,写入的是不同的物理地址,这样就不会冲突了。

于是,这里就引出了两种地址的概念:                                                                                           

 ●我们程序所使用的内存地址叫做虚拟内存地址                                                                               

 ●实际存在硬件里面的空间地址叫物理内存地址                                                                               

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

二 内存分段 

虚拟内存分为若干个分段,其中包括代码段、数据段、栈段和堆段。这些部分通常是在程序执行过程中被动态分配和管理的,如果想再多分几个分段,可以通过操作系统提供的内存管理接口或者其他一些方法。

2.1内存分段原理

分段机制下的虚拟内存由两部分组成,段选择因子和段内偏移量。                                                 

●段选择因子:通常由索引和特权级别组成,索引部分指定了要访问的段在段描述符表中的索引位置。CPU使用段选择因子来定位段描述符表中对应的段描述符。                                                   

●段描述符表:分为全局描述符表和局部描述符表。每个进程都有自己的局部描述符表,局部描述符表存储在进程的PCB中。局部描述符表存储了进程的段描述符信息,比如段描述符的地址等。全局描述符表存在内存的特定位置,他管理所有进程的局部描述符表。                                             

●段描述符:段描述符包含了关于一个段的重要信息,如段的基地址(起始地址),段的长度,段的访问权限(包括对该段的访问权限(读、写、执行)、段的类型(数据段、代码段)、特权级别等信息)等。                                                                                                                                         

●段内偏移量:段内偏移量是相对于所选段起始地址的偏移量,通常是一个32位的值,用于确定所选段内的具体地址。实际偏移量和程序中引用的偏移量相加,得到段内偏移量,将段选择子中的偏移量字段左移 4 位(乘以 16)得到实际偏移量。段内偏移量应该处于8和段边界区间,段边界区间保存在各个段的段描述符中。 最终的物理地址是段基地址加上段内偏移量。

2.2 内存分段的问题

内存分段机制的问题: 1)内存碎片问题 2)内存交换的效率低下问题   

 为什么会出现这样的问题??                                                                                                        我们来看看这样一个例子。假设有 1G 的物理内存,用户执行了多个程序,其中: 游戏占用了 512MB 内存,浏览器占用了 128MB 内存,音乐占用了 256 MB 内存。 这个时候,如果我们关闭了浏览器,则空闲内存还有 1024 - 512 - 256 = 256MB。 如果这个 256MB 不是连续的,被分成了两段 128 MB 内存,这就会导致没有空间再打开一个 200MB 的程序。         

这里的内存碎片的问题共有两处地方:                                                                                           

1)外部内存碎片,也就是产生了多个不连续的小物理内存导致新的程序无法被载。                       

 2)内部内存碎片,程序所有的内存都被装载到了物理内存,但是这个程序有部分的内存可能并不是很常使用,这也会导致内存的浪费。                                                                                           针对上面两种内存碎片的问题,解决的方式会有所不同:                                                               

●解决外部内存碎片的问题就是内存交换。可以把音乐程序占用的那 256MB 内存写到硬盘上,然后再从硬盘上读回来到内存里。不过再读回的时候,我们不能装载回原来的位置,而是紧紧跟着那已经被占用了的 512MB 内存后面。这样就能空缺出连续的 256MB 空间,于是新的 200MB 程序就可以装载进来。这个内存交换空间,在 Linux 系统里,也就是我们常看到的 Swap 空间,这块空间是从硬盘划分出来的,用于内存与硬盘的空间交换。                                                                   

●再来看看,分段为什么会导致内存交换效率低的问题? 对于多进程的系统来说,用分段的方式,内存碎片是很容易产生的,产生了内存碎片,那不得不重新 Swap 内存区域,这个过程会产生性能瓶颈。 因为硬盘的访问速度要比内存慢太多了,每一次内存交换,我们都需要把一大段连续的内存数据写到硬盘上。                                                         

所以,如果内存交换的时候,交换的是一个占内存空间很大的程序,这样整个机器都会显得卡顿。 为了解决内存分段的内存碎片和内存交换效率低的问题,就出现了内存分页。

 三 内存分页

3.1 内存分页背景     

解决内存分段的缺陷,分段的好处就是能产生连续的内存空间,但是会出现内存碎片和内存交换的空间太大的问题。 要解决这些问题,那么就要想出能少出现一些内存碎片的办法。另外,当需要进行内存交换的时候,让需要交换写入或者从磁盘装载的数据更少一点,这样就可以解决问题了。这个办法,也就是内存分页。

3.2 内存分页机制

分页是把整个虚拟和物理内存空间切成一段段固定尺寸的大小。这样一个连续并且尺寸固定的内存空间,我们叫页。在 Linux 下,每一页的大小为 4KB。                                                                   

●虚拟地址与物理地址之间通过页表来映射。                                                                                   

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

分页是怎么解决分段的内存碎片、内存交换效率低的问题?                                                           由于内存空间都是预先划分好的,也就不会像分段会产生间隙非常小的内存,这正是分段会产生内存碎片的原因。而采用了分页,那么释放的内存都是以页为单位释放的,也就不会产生无法给进程使用的小内存。 如果内存空间不够,操作系统会把其他正在运行的进程中的「最近没被使用」的内存页面给释放掉,也就是暂时写在硬盘上,称为换出。一旦需要的时候,再加载进来,称为换入。所以,一次性写入磁盘的也只有少数的一个页或者几个页,不会花太多时间,内存交换的效率就相对比较高。更进一步地,分页的方式使得我们在加载程序的时候,不再需要一次性都把程序加载到物理内存中。我们完全可以在进行虚拟内存和物理内存的页之间的映射之后,并不真的把页加载到物理内存里,而是只有在程序运行中,需要用到对应虚拟内存页里面的指令和数据时,再加载到物理内存里面去。                       

分页机制下,虚拟地址和物理地址是如何映射的?                                                                         在分页机制下,虚拟地址分为两部分,页号和页内偏移。页号作为页表的索引,页表包含物理页每页所在物理内存的基地址,这个基地址与页内偏移的组合就形成了物理内存地址, 总结一下,对于一个内存地址转换,其实就是这样三个步骤:                                                                           

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

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

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

四 段页式内存管理

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

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

●接着再把每个段划分为多个页,也就是对分段划分出来的连续空间,再划分固定大小的页这样,地址结构就由段号、段内页号和页内位移三部分组成。                                                                   

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

●段页式地址变换中要得到物理地址须经过三次内存访问:                                                                         第一次访问段表,得到页表起始地址;                                                                                               第二次访问页表,得到物理页号;                                                                                                     第三次将物理页号与页内位移组合,得到物理地址。 可用软、硬件相结合的方法实现段页式地址变换,这样虽然增加了硬件成本和系统开销,但提高了内存的利用率。

四 Linux内存管理

在 Linux 操作系统中,虚拟地址空间的内部又被分为内核空间和用户空间两部分,不同位数的系统,地址空间的范围也不同。比如最常见的 32 位和 64 位系统。                                                 

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

●64 位系统的内核空间和用户空间都是 128T。                                       

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

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

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

●虽然每个进程都各自有独立的虚拟内存,但是每个虚拟内存中的内核地址,其实关联的都是相同的物理内存。这样,进程切换到内核态后,就可以很方便地访问内核空间内存。虽然每个进程都有自己的虚拟内存,但是在内核空间中,关联的确实是相同的物理内存。也就是说,在物理内存中,操作系统内核的代码和数据结构通常有固定的位置或范围。这意味着,无论哪个进程切换到内核态,都可以访问相同的内核地址空间。内核空间的内容有操作系统内核的各种功能和服务的实现代码,如进程调度、内存管理。内核空间中还存储了许多数据结构,用于管理系统资源和状态,这些数据结构包括PCB等,还有一些内容

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

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

相关文章

最简单的 AAC 音频码流解析程序

最简单的 AAC 音频码流解析程序 最简单的 AAC 音频码流解析程序原理源程序运行结果下载链接参考 最简单的 AAC 音频码流解析程序 参考雷霄骅博士的文章:视音频数据处理入门:AAC音频码流解析 本文中的程序是一个AAC码流解析程序。该程序可以从AAC码流中…

Linux(05) Debian 系统修改主机名

查看主机名 方法1:hostname hostname 方法2:cat etc/hostname cat /etc/hostname 如果在创建Linux系统的时候忘记修改主机名,可以采用以下的方式来修改主机名称。 修改主机名 注意,在linux中下划线“_”可能是无效的字符&…

数据结构(初阶)第一节:数据结构概论

本篇文章是对数据结构概念的纯理论介绍,希望系统了解数据结构概念的友友可以看看,对概念要求不高的友友稍做了解后移步下一节: 数据结构(初阶)第二节:顺序表-CSDN博客 正文 目录 正文 1.数据结构的相关概…

qqqqqqq

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起学习和分享Linux、C、C、Python、Matlab,机器人运动控制、多机器人协作,智能优化算法,滤波估计、多传感器信息融合,机器学习,人工智能等相关领域的知识和…

Polardb MySQL 产品架构及特性

一、产品概述; 1、产品族 参考:https://edu.aliyun.com/course/3121700/lesson/341900000?spma2cwt.28120015.3121700.6.166d71c1wwp2px 2、polardb mysql架构优势 1)大容量高弹性:最大支持存储100T,最高超1000核CPU&#xff0…

open Gauss 数据库-03 openGauss数据库维护管理指导手册

发文章是为了证明自己真的掌握了一个知识,同时给他人带来帮助,如有问题,欢迎指正,祝大家万事胜意! 目录 前言 openGauss数据库维护管理 1 操作系统参数检查 1.1 实验介绍 1.2 场景设置及操作步骤 2 openGauss 运…

认识什么是Webpack

目录 1. 认识Webpack 1.1. 什么是Webpack?(定义) 1.2. 使用Webpack 1.2.1. 需求 1.2.2. 步骤 1.3. 入口和出口默认值 1.3.1. 需求代码如下 2. 修改Webpack打包入口和出口 2.1. 步骤: 2.2. 注意 3. Webpack自动生成html文件 3.1.…

D-迷恋网游(遇到过的题,做个笔记)

我的代码&#xff1a; #include <iostream> using namespace std; int main() {int a, b, c; //a表示内向&#xff0c;b表示外向&#xff0c;c表示无所谓cin >> a >> b >> c; //读入数 if (b % 3 0 || 3-b % 3 < c) //如果外向的人能够3人组成…

真·面试题总结——JVM虚拟机

JVM虚拟机 JVM虚拟机规范与实现 JVM虚拟机规范 JVM虚拟机实现 JVM的常见实现 JVM虚拟机物理架构 JVM虚拟机的运转流程 JVM类加载过程 JVM类加载器及类加载器类型 JVM类加载器双亲委派机制 JVM运行时数据区的内存模型 JVM运行时数据区的内存模型&#xff1a;程序计数器…

蓝桥杯第八届c++大学B组详解

目录 1.购物单 2.等差素数列 3.承压计算 4.方格分割 5.日期问题 6.包子凑数 7.全球变暖 8.k倍区间 1.购物单 题目解析&#xff1a;就是将折扣字符串转化为数字&#xff0c;进行相加求和。 #include<iostream> #include<string> #include<cmath> usin…

vue2 列表一般不使用索引删除的原因

在 Vue 中使用索引来删除列表项可能会导致一系列问题&#xff0c;尤其是在处理动态列表时。以下是一些可能的问题和相应的例子&#xff1a; 1. 数据不一致问题 当你使用索引来删除列表中的某个项时&#xff0c;如果列表中的其他项发生了变化&#xff08;比如新增或重新排序&a…

编译时提示存在多个默认构造函数的错误怎么解决呢?

c程序中&#xff0c;如果编译器提升存在多个默认构造函数怎么解决呢&#xff1f; class Date { public:Date(){_year 1900;_month 1;_day 1;}Date(int year 1900, int month 1, int day 1){_year year;_month month;_day day;} private:int _year;int _month;int _day…

chromium源码学习-调试日志 LOG

在学习 chromium 源码时&#xff0c;我们经常需要增加调试日志&#xff0c;常见的用法一般是 LOG(INFO) << "调试信息";其中 INFO 代表当前这条日志的级别&#xff0c;使用的时候就是输入 INFO 就行。接下来我们在探索下这个宏背后的内容。 一、基本用法 LO…

读所罗门的密码笔记08_共生思想(下)

1. 机器判断 1.1. 在生活的各个领域&#xff0c;机器正在我们无意识的情况下做出更多的决定 1.1.1. 我们看到的新闻会塑造我们的观点和行动&#xff0c;它们是根据我们过去行为中所表达的倾向&#xff0c;或者其他同类人的行为而生成的 1.2. …

K-均值聚类算法

K-均值聚类算法是一种常用的无监督学习算法&#xff0c;用于将数据集分成 K 个簇。该算法的主要思想是通过迭代的方式将数据点分配到离它们最近的簇中&#xff0c;并更新簇的中心点&#xff0c;直到满足某个停止条件为止。 以下是 K-均值聚类算法的基本步骤&#xff1a; 初始化…

【热门话题】WebKit架构简介

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 WebKit架构简介一、引言二、WebKit概览1. 起源与发展2. 模块化设计 三、WebCore…

软考高级架构师:流水线的概念和例题

作者&#xff1a;明明如月学长&#xff0c; CSDN 博客专家&#xff0c;大厂高级 Java 工程师&#xff0c;《性能优化方法论》作者、《解锁大厂思维&#xff1a;剖析《阿里巴巴Java开发手册》》、《再学经典&#xff1a;《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

基于SSM的“任务发布接收平台”的设计与实现(源码+数据库+文档+PPT)

基于SSM的“任务发布接收平台”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SSM 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 登录界面 前台界面 收藏界面 留言管理界面 任务管理界面 订…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《考虑灵活性供需平衡的新型电力系统长短期储能联合规划》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

file_get_contents(‘php://input‘); 这个postman要如何传参

在 Postman 中传递参数给 file_get_contents(php://input); 是通过请求的 Body 部分来实现的。使用 Postman 进行 API 接口测试时&#xff0c;可以按照以下步骤来传递参数&#xff1a; 打开 Postman 并创建一个新的请求。在请求的 URL 地址栏输入你的 API 地址。选择请求方法为…