学习系统编程No.5【虚拟地址空间】

news2024/10/7 14:21:59

引言:

北京时间:2023/2/22,离补考期末考试还有5天,不慌,刚午觉睡醒,闹钟2点20,拖到2点50,是近以来,唯一一次有一种睡不醒的感觉,但是现在却没有精神,因为听了一首歌(当然洗脸更重要),天后孙燕姿的《直来直往》,三天170遍,上头,感兴趣的小伙伴可以去尝试一下哦!上篇博客我们学习了系统中环境变量等知识,现在我们接着系统环境变量的知识,来学习一下进程地址空间的相关知识

在这里插入图片描述

虚拟地址空间

什么是进程地址空间呢?让我们带着这个疑问,一步一步的来探索吧!首先我们在以前的知识中,学习过如果创建子进程(fork函数),并且明白进程其中的一个特性:独立性

所以明白这两点,看下图中的代码和运行结果,就可以很轻松的搞定
在这里插入图片描述
搞定了上述的代码和运行结构,此时我们就可以通过下述的代码和运行结果来引出进程地址空间的一个小知识点了,就是为什么父进程和子进程之间的地址是一样的呢?
在这里插入图片描述
看到上述代码,我们可以发现,利用进程的独立性,此时一个进程改变全局变量,并不会影响到另一个进程当中的全局变量,了解了这点,突然觉得进程真的是非常的神奇,看起来就是一个独立的个体一样,当然更神奇的是,这两个进程的地址是一样的,为了探索地址一样的问题,此时我们就找到了进程地址空间的切入点,从父子进程拥有相同的地址空间问题,引出地址空间的概念,接下来就让我们再次探索地址空间的知识吧!

复习有关内存的知识

当我们从上述例子之中得到了有关虚拟地址空间的概念之后,此时我们肯定是会有疑问的,比如:最大的疑问,为什么要有虚拟地址空间?电脑中不是存在内存(物理地址),为什么不使用内存(物理地址)呢?
此时在学习虚拟地址空间的概念之前,我们就先来复习一下以前学过的有关内存的相关知识,首先内存也叫存储器,是电脑中非常重要的硬件组成部分,因为有了存储器,才使电脑拥有的记忆功能,并且存储器可以分为内存和外存(磁盘),这里我们具体讲一讲什么是内存,内存也还可以进行细分,分为只读内存(ROM)和随机存取内存(RAM),注意:此时的ROM指的就是硬盘,也是属于内存中的一种哦!搞清了之间的关系,我们来看看存储器的组成,存储器主要是采用半导体器件和磁性材料构成,存储器的最小单元是存储元(由一个双稳态半导体电路或一个CMOS晶体管或磁性材料构成),然后由若干个存储元可以构成一个存储单元,最后存储器就是由许多的存储单元构成的,并且每一个存储单元可存放一个字节的空间,所以如果你的机器是一个32位的机器,此时就物理上就有32根地址线,有了32根地址线,当电脑进行通电的时候,就会产生32个正电/负电(1/0),此时就使我们的电脑有了32个由1/0构成的二进制序列,通过组合此时就有2^32 次方中可能,有了这么多的组合,此时就可以把这些组合和我们的存储元给结合在一起,通过各种二进制序列组合,给存储元编号,这种就使我们拥有的地址的概念,所以如果是一个32位的机器,此时他就有43亿左右(2的32次方)存储单元地址,此时每个存储单元存放一个字节,此时该存储器就拥有的4G左右的内存空间。(所以也就导致了,你如果想要使用内存,就必须把对应的信息给转换成二进制序列,这样才可以在内存中这么多的地址中寻找到对应的数据)如下图所示:

看到这幅图,我相信大家对内存的概念,应该会有更深层次的体会和认识吧!所以在我们电脑中的内存,也就是物理内存,本质上就是如上图的形式进行数据的存储。

为什么要有虚拟地址空间?

在了解为什么要有虚拟地址空间之前,我们先来谈谈操作系统的特性和基本功能,所有应用程序对硬件的操作都必须通过操作系统实现,并且为了防止硬件被失控的应用程序滥用向应用程序提供简单一致的机制来控制复杂而又通常大不相同的低级硬件设备;操作系统此时需要通过抽象进程、虚拟内存和文件等概念来实现,如下图:
在这里插入图片描述
文件是对I/O设备的抽象表示,虚拟内存是对主存和磁盘I/O设备的抽象表示,进程则是对处理器、主存和I/O设备的抽象表示

明白了上述知识,引出关键概念:进程是对处理器、主存和I/O设备的抽象

所以此时就有了,当我们运行一个程序的时候,操作系统会提供一个假象,就好像系统上只有这个程序在运行。程序看上去是在独占地使用处理器、主存和I/O设备,处理器看上去就好像是在不间断地一条一条地执行该程序中的指令,即该程序的代码和数据是系统内存中唯一的对象;所以进程是操作系统对一个正在运行的程序的一种抽象,在一个系统上可以同时运行多个进程,而每个进程都好像在独占地使用硬件。

所以此时我们就明白了,一个进程在运行时,是独占所有的系统资源的

有了上述的知识,此时就可以引出我们想要学习的重点知识,为什么要有虚拟地址空间,理由如下:

  1. 在虚拟内存出现之前,程序寻址用的都是物理地址,因此程序能寻址的范围是有限的,具体寻址的范围是2^32也就是4G;
  2. 并且如果没有虚拟内存,那么每次运行进程就需要分配4G的物理内存给该进程使用,因为进程是对处理器、主存和I/O设备的抽象,是独占使用内存的,运行一个进程就需要把我们所有的内存供给其使用;
  3. 并且由于物理内存是有限的,当有多个进程要执行的时候,每个进程都需要分配4G内存,独占的使用所有的硬件,所以当内存被分配完之后,没有得到分配资源的进程就只能等待,当另一个进程执行完后,再将等待的进程装入内存,这种方式执行进程效率是非常低下的;
  4. 并且由于指令都是直接访问物理内存,那么进程就可以修改其他进程的数据,甚至会修改内核地址空间的数据,所以这种直接访问物理内存的方式是非常的可怕的,并且因为内存是随机分配的,所以程序运行的地址也是不正确的。

所以根据以上的种种原因,我们就需要引入一个叫虚拟地址空间的概念来尝试解决这些问题。

什么是虚拟地址空间

首先根据上述的知识,我们知道了为什么要有虚拟地址空间的概念,所以此时我们就正式来谈谈什么是虚拟地址空间,概念:每个进程创建加载的时候,会被分配一个大小为4G的连续的虚拟地址空间,虚拟的意思就是,其实这个地址空间时不存在的,仅仅是每个进程“认为”自己拥有4G的内存,而实际上,它用了多少空间,操作系统就在磁盘上划出多少空间给它。
通过概念,我们可以知道,虚拟地址空间的本质在我们的电脑中是不存在的(不像物理内存那样,拥有存储单元,每个单元一个字节),它是通过映射的形式将对应的数据存储在内存之中,具体说起来较为复杂,这里先不做了解。让我们看图来加深对虚拟地址空间的认识,如下图:

每个进程看到的虚拟地址空间由大量准确定义的区构成,如上图所示,每个区都有专门的功能

此时我们就来一一简单介绍一下这些区域的功能
  1. 程序代码和数据区(代码段):例如此时我们在执行一个C程序,对所有的进程来说,代码是从同一固定地址开始,紧接着的是和 C全局变量相对应的数据位置存放,代码和数据区是直接按照可执行目标文件的内容初始化的

  2. 堆:代码和数据区后紧随着的是运行时堆。代码和数据区在进程一开始运行时就被指定了大小,与此不同,当调用像 malloc 和free 这样的C标准库函数时,堆可以在运行时动态地扩展和收缩

  3. 共享库:大约在地址空间的中间部分是一块用来存放像C标准库和数学库这样的共享库的代码和数据的区域。共享库的概念非常强大,这里先简单介绍。

  4. 栈:位于用户虚拟地址空间顶部的是用户栈,编译器用它来实现函数调用。和堆一样,用户栈在程序执行期间可以动态地扩展和收缩。特别地,每次我们调用一个函数时,栈就会增长;从一个函数返回时,栈就会收缩。

  5. 内核虚拟内存:地址空间顶部的区域是为内核保留的。不允许应用程序读写这个区域的内容或者直接调用内核代码定义的函数。相反,它们必须调用内核来执行这些操作。

总:虚拟内存的运作需要硬件和操作系统软件之间精密复杂的交互,包括对处理器生成的每个地址的硬件翻译。基本思想是把一个进程虚拟内存的内容存储在磁盘上,然后用主存作为磁盘的高速缓存

如何管理进程地址空间

通过上述的知识,此时我们明白了什么是虚拟地址空间,所以当我们想要执行一个程序的时候,该程序被加载到内存中,内存生成一个对应的PCB(task_struct)结构体,来管理该程序对应的代码和数据,此时的PCB中就存放这该进程的全部信息,此时就可以明白,我们的操作系统管理任何东西,都是通过先描述,再组织的方式,进行管理的(总而言之就是生成一个有合适成员变量的结构体),所以此时操作系统管理进程地址空间,也是通过先描述,再组织的形式,这样此时就又有了一个新的结构体( mm_struct ),专门给进程使用的结构体,也就是操作系统专门用来管理进程的结构体,所以,我们上述所说的进程可以独占的使用系统资源,本质上就是因为其被操作系统通过先描述,再组织的方式形成了一个结构体,就是一个内核数据结构而已

pcb和mm_struct和进程地址空间具体关系请看下图(搞清关系就行):
在这里插入图片描述

从上图可以看出,我们的task_struct结构体中是有一个结构体指针,指向着mm_struct进程结构体的

从物理内存理解虚拟内存

明白了上述的知识,此时我们知道,无论内存怎样虚拟化,最后肯定是都要放在物理内存之中,所以想要深入了解虚拟内存,此时就需要从物理内存的方面去理解,首先文章开始,我们已经复习过了物理内存的知识,这里就不多做介绍了,我们只要明白32位的机器,物理内存是通过2^32个字节的地址空间构成就行,并且如图,我们可以看出,物理内存是连续的,线性的,每个字节的空间是独立的(一个地址代表一个字节),所以当我们此时开辟了一个整形类型(int),此时就需要在线性内存中占4个连续的地址(因为一个地址只代表一个字节),所以本质上,开辟一个整形,向线性内存申请了4个不同的地址(地址连续),理解这个,我相信大家都有疑问,因为平时我们在调式,看变量地址的时候,并不可以看到4个地址,只能看到一个地址,所以此时就需要把这两个概念给结合起来理解,最终得出,本质上是4个地址,但是由于这些地址是连续的,方便查找的,所以,操作系统只是把4个地址中的最下面(编号最小)的那个地址(线性)给给我们,剩下的3个地址,是默认存在,只是你看不到而已;并且此时系统想要通过我们看到的那一个地址,找到剩下3个地址的话,就需要识别出该变量的类型(区别double、float、long),通过各个类型规定的字节大小,依次从内存中的地址向后读取,例:int,我们就再向内存读取三个字节,读取三个字节后就停止,double,就再向后读取7个字节,这样,我们就可以很好的把物理内存的地址给管理使用起来。

总:地址空间是线性结构

当我们了解到了,地址空间是线性的,那么可以知道我们的虚拟地址空间应该也是线性的,所以此时就如上述的虚拟地址空间图一样,我们可以把虚拟地址空间给划分成一个一个的区域,实现每个区域执行不同的功能,又因为,我们的操作系统,会将进程给抽象成一个结构体(mm_struct),如下图:

此时我们就知道,操作系统中的进程结构体就是通过一个一个的作用区域的开始(start)和结束(end)来控制进程虚拟地址空间,例如:当code_start = 100,code_end = 200,此时在该代码段中的100和200之间的区域表示的就是我们的虚拟地址,所以虚拟地址空间就是一个线性的结构,并且此时我们可以把这个结构通过结构体的方式定义成一个具有区域划分的新结构,并且可以通过改变某个区域的开始(start)和结束(end),来改变该区域的大小,例如:虚拟地址中的栈区和堆区的扩大缩小,就是通过这种形式来完成的。(注:只是简单理解,原理不会错,只是系统操作起来远复杂)

浅谈数据从虚拟地址空间拷贝到物理内存

这个问题是比较复杂的,感兴趣的同学请看这篇博客大佬博客

每个进程启动之后,操作系统都会自动生成一个该进程的页表用于存储的是虚拟地址和物理地址的映射,然后通过和CPU上的 MMU 进行交互,把虚拟地址翻译成物理地址,进而存储在物理内存之中(原理就是这样,但是实际复杂n倍)。

如下图所示:
在这里插入图片描述
通过上图,我们可以发现,为什么修改子进程的值,不会改变父进程的值,原因就是:当修改子进程中的值时,内存会自己重新开辟一块空间给你,然后把你的页表映射关系给改变,此时页表映射了位置就是新的空间,此时就可以实现子进程的值改变,并且因为页表是独立的,所以改变的只是物理内存和页表的映射关系,不会改变原虚拟地址中的值,所以此时就很好的证明了,子进程和父进程同一地址,但不同值的问题,并且因为此时在物理内存中开辟了两块空间,所以也解释了父子进程有两个返回值的问题。

使用虚拟地址空间的好处

1.防止地址随意访问,保护物理内存与其它进程
2.将进程管理和内存管理进行解耦合(本质上是为了提高操作系统效率和内存使用效率)
3.可以让进程以统一的视角,看待自己的代码和数据(进而忽略内存中的地址位置),提高运行效率

在这里插入图片描述

总结:系统中无论是什么东西,本质就是为了提高操作系统的效率,虚拟地址soso

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

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

相关文章

【Spring Cloud Alibaba】007-Nacos 配置*

【Spring Cloud Alibaba】007-Nacos 配置* 文章目录【Spring Cloud Alibaba】007-Nacos 配置*一、概述1、概述2、对比 spring cloud config二、基本使用1、在管理界面新建配置2、启动权限3、 搭建 nacos-config 服务第一步:引入依赖第二步:修改 yaml 配置…

交叉编译 MQTT/Mosquitto

交叉编译 MQTT/Mosquitto 概述 Eclipse Mosquitto 是一个开源(EPL/EDL许可)消息代理,它实现了 MQTT 协议版本 5.0、3.1.1 和 3.1。Mosquitto 重量轻,适用于从低功耗单板计算机到全服务器的所有设备。 MQTT 协议提供了一种使用发…

【Linux】-- 开发工具(vim、gcc、g++、make/Makefile)

目录 Linux编辑器-vim使用 vim的基本概念 vim的简单配置 vim的基本操作 vim进阶命令集 插入模式 从插入模式切换为命令模式 移动光标 删除文字 复制 替换 撤销上一次操作 更改 跳至指定的行 vim底行模式命令集 列出行号 跳到文件中的某一行 查找字符 保存文…

黑马程序员 Java 项目《瑞吉外卖》

教程链接:https://www.bilibili.com/video/BV13a411q753 Gitee 仓库:https://gitee.com/weixinyang1980/reggie_take_out 运行视频: 瑞吉外卖后台运行视频 瑞吉外卖用户端运行视频 目录开发环境搭建数据库环境搭建Maven 项目搭建后台登录功能…

通过连接另一个数组的子数组得到一个数组

给你一个长度为 n 的二维整数数组 groups ,同时给你一个整数数组 nums 。 你是否可以从 nums 中选出 n 个 不相交 的子数组,使得第 i 个子数组与 groups[i] (下标从 0 开始)完全相同,且如果 i > 0 ,那么…

Python四大主题之一【 Web】 编程框架

目前Python的网络编程框架已经多达几十个,逐个学习它们显然不现实。但这些框架在系统架构和运行环境中有很多共通之处,本文带领读者学习基于Python网络框架开发的常用知识,及目前的4种主流Python网络框架:Django、Tornado、Flask、Twisted。 …

论文复现:风电、光伏与抽水蓄能电站互补调度运行(MATLAB-Yalmip全代码)

论文复现:风电、光伏与抽水蓄能电站互补调度运行(MATLAB-Yalmip全代码) 针对风电、光伏与抽水蓄能站互补运行的问题,已有大量通过启发式算法寻优的案例,但工程上更注重实用性和普适性。Yalmip工具箱则是一种基于MATLAB平台的优化软件工具箱,被广泛应用于工程界优化问题和…

JVM知识最强总结

类加载运行全过程1.java.exe调用底层jvm.dll创建java虚拟机,2.创建引导类加载器实例,3.完成实例的创建后需要一个启动器,这个是通过sun.misc.launcher类实现,通过该类的getLaunch()方法启动,4.然后调用该类的getClassL…

梳理数字资产,亚马逊云科技助力致盛咨询带来药物研发新福音

作为医疗保健领域的咨询公司,ZS需要保证服务可靠性、敏捷性和安全性的同时,获得经济效益。亚马逊云科技丰富的云服务产品简化了ZS基础架构的搭建,为ZS节省了大量的人力与资金成本。同时,缩短了ZS扩展基础设施的周转时间&#xff0…

一篇文章读懂Android Framework

本文旨在将Framework的框架描绘出来,希望抛砖引玉,对于读者有一定的帮助。前言写在前面:1、有没有必要学习linux内核?我认为是很有必要的。学习linux内核有助于我们加深对一些概念的理解,比如“进程”、“线程”。推荐…

JVM18运行时参数

4. JVM 运行时参数 4.1. JVM 参数选项 官网地址:https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html 4.1.1. 类型一:标准参数选项 > java -help 用法: java [-options] class [args...](执行类)或 java [-options] -jar …

图解LeetCode——剑指 Offer 47. 礼物的最大价值

一、题目 在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值&…

mongoTemplate Aggregation 多表联查 排序失效问题解决

目录说明说明 接着上一个文章的例子来说:mongoTemplate支持多表联查 排序 条件筛选 分页 去重分组 在按照上一个demo的代码执行后,可能会发生排序失效的问题,为什么说可能呢?每个人负责业务不同,不可能是最简单的dem…

树莓派CM4基础设置

安装系统1.1 软件和硬件准备硬件:CM4(4GB DDR32GB EMMC 板载WIFI和蓝牙)CM4-to-Pi4-Adapter软件:Raspberry Pi或者 Win32DiskImagerRaspberry Pi下载链接:点击直接下载Win32DiskImager下载链接:链接&#x…

el-table大数据量渲染卡顿问题

1、场景描述 在项目开发中,遇到在表格中一次性加载完的需求,且加载数量不少,有几百几千条,并且每条都可能有自己的下拉框,输入框来做编辑功能,此时普通的el-table肯定会导致浏览器卡死,那么怎么…

【Python小程序】怀旧经典 | 特色玩法,代码版本的钢琴小游戏了解下?初学钢琴,能提高双手协调与反应能力哦~(源码分享)

导语 哈喽,我是木木子鸭! 最近给大家悄悄的更新了一些关于爬虫的内容呢~有想学习爬虫的小可爱可以学习一整子啦。 今天来给大家写一款界面化的(Tkinter)电子钢琴小程序。 ​ 所有文章完整的素材源码都在👇&#x1…

Qt程序使用路径方式和注意事项

Qt程序使用路径方式和注意事项 更多精彩内容👉个人内容分类汇总 👈👉Qt开发经验 👈文章目录Qt程序使用路径方式和注意事项[toc]前言一、Windows下Qt程序使用路径1.准备工作2.测试结果二、Linux下Qt程序使用路径1.准备工作2.测试结…

Python如何实现自动登录和下单的脚本,请看selenium的表演

前言 学python对selenium应该不陌生吧 Selenium 是最广泛使用的开源 Web UI(用户界面)自动化测试套件之一。Selenium 支持的语言包括C#,Java,Perl,PHP,Python 和 Ruby。目前,Selenium Web 驱动…

某餐厅系统网络故障分析案例

背景 针对食堂经营企业,某堂食软件为客户提供优化堂食就餐流程、提高食堂服务水平和管理效率。 某上海客户使用该堂食系统,在就餐高峰时段,总是出现支付、点餐等操作缓慢,动辄一个操作需要等待几十秒。该客户联系软件厂商&#…

浮点数在内存中的存储——“C”

各位CSDN的uu们你们好呀,今天,小雅兰的内容是浮点数在内存中的存储,昨天我们已经写过了整型在内存中的存储,那么,浮点数在内存中是怎样存储的呢?现在,就让我们进入浮点数在内存中的存储的世界吧…