【Linux进阶之路】进程(中)—— 进程地址空间

news2024/10/2 16:17:09

文章目录

  • 一、 进程地址空间
    • 1.概念引入
    • 2.基本概念
    • 3.深入概念
      • 3.1 初识信息交互
      • 3.2 区域划分
      • 3.3 进程地址空间
      • 3.4 再识页表
        • 缺页中断
        • 进程挂起
  • 总结

一、 进程地址空间

1.概念引入

  • 指针指向的地址是内存中的地址吗?下面我们用一个实验来证明一下。
  1. 先来写程序看一下程序的地址分布。

在这里插入图片描述

  1. 查看并分析运行结果:

在这里插入图片描述

  • 可见是符合预期的结果的。

说明:在Windows下的内存分布可能不一样,实际情况要看具体的操作系统,如果细究,在各个区域定义一个变量,打印其地址进行验证。

  1. 创建子进程修改一个指定变量,然后查看其地址。
  • 实验代码
    在这里插入图片描述
    再回顾一下之前的知识:
  1. fork创建子进程,对父进程返回子进程的pid,对子进程返回0。
  2. 父子进程对只读的代码共享(写时拷贝)。

说明:这里我们是对id变量的修改。

  • 运行结果与分析
    在这里插入图片描述

说明:

  1. 物理地址是内存某一位置的实际地址,且物理地址是唯一的(因为就一块内存)。
  2. 此处假设是物理地址,那么其中的值必然是相同的,但是这里不同,显然不是物理地址。
  • 结论与引入:既然不是物理地址,那存放的是什么地址呢?我们一般称之为虚拟地址/线性地址
  • 那如何解释上述地址相同的现象呢?

2.基本概念

在这里插入图片描述

  • 虚拟地址存放的空间,我们一般叫做进程地址空间,如上图。

说明:
程序中变量存的是虚拟地址,不是物理地址,那我们之前学的就是错的吗?
解释:

  1. 很显然不是,是因为所占的角度不一样。
  2. 首先写代码时我们是站在应用层的角度进行考虑的;
  3. 其次我们目前学操作系统,是从偏硬件的角度进行理解的;
  4. 而且学了进程地址空间,只会让我们理解的更深,更加偏向底层。
  5. 因此我们之前学的不是错的,只是理解尚浅。

 而且虚拟地址一定能够转换为物理地址,如何转换呢?—— 页表

  • 原理:类似哈希表的结构,其虚拟地址->物理地址。

现在我们尝试画图解决一下之前的问题:

  1. 对上述程序的指定行进行解读:

在这里插入图片描述

  • fork创建完子进程未返回。
  1. 通过对父进程进行拷贝(浅拷贝),对代码和数据进行拷贝,生成跟父进程几乎一样(数据与代码的虚拟地址完全相同)的进程地址空间(独有的pid不一样)。
  2. 对页表进行拷贝,生成一模一样的页表。
  3. 此时的父进程与子进程的id变量指向同一块物理地址。

图解:
在这里插入图片描述

  • fork返回,对子进程返回0,对父进程返回子进程的pid。
  1. 写入过程中由于父子进程指向同一块物理空间,因此操作系统给子进程再申请一块物理空间,并将子进程页表的物理地址进行修改,虚拟地址不变。
  2. 对子进程id变量的物理空间写入0。
  3. 对父进程id变量的物理空间写入子进程的pid。

图解:
在这里插入图片描述

  • 那如何解释上述地址相同的现象呢?

3.深入概念

3.1 初识信息交互

问题1:CPU 、内存、输入输出设备如何进行交互?
解释:

  1. 通过"线"进行交互。
  2. 线细分为三种,地址总线,数据总线,控制总线。
  3. 线简单分为两种,CPU与内存的线称为系统总线,内存与输入输出设备的线称为IO总线。

问题2:线是什么?为什么要用线?
解释:

  1. 线简单理解就是用于进行数据传输的通道。
  2. 线的主要作用是确保计算机组件之间能够进行协调通信与合作。
  3. 用线的另一个原因在于生产各个部件的厂商不同,但各个部件又需要结合才能使用,因此只需用线将不同的部件结合,在整机效率不变的基础上提升了生产效率。

问题3: 从硬件的角度解释,计算机是如何产生01序列的?
解释:

  1. 低电频产生低位0,高电频产生高位1。
  2. 每根线可产生0/1,两种电频,在32位(32根线)的机器下,能产生232种电频信号,因此32位机器最多存放232 byte的数据也就是4GB。

3.2 区域划分

 前面我们只是初始概念,下面我们通过问题来对概念进一步的理解。

  1. 区域划分是什么?请结合进程地址空间进行思考。

下面博主讲个故事引入理解:

 回想起美好而又纯洁的青春,一定发生过类似这样的故事。

小帅:hello!大家好,我是小帅。

在这里插入图片描述
小美:hi ! 大家好,我是小美。
在这里插入图片描述

  • 那些同桌之间发生的事:

 小帅与小美是同桌,两个人用的是双人桌,小帅有点喜欢小美,想着吸引小美的注意,于是就趁着小美不注意,就用胳膊戳小美,惹的小美不知该如何是好,于是抱着忍一时风平浪静的心态就没搭理小帅,小帅看着无动于衷的小美,心想按照剧本应该理一下我猜对啊,难道得玩把大的?年轻的我们总是想着吸引喜欢人的注意,而不知道出发的方式,于是小帅又趁着小美不注意,正想要顺走小美最喜欢的笔,此时被小美发现了,小美这下子忍不住了,想着老娘不发威,你当老娘是病猫,于是揪着小帅的耳朵,给小帅了一顿"奖励" , 小帅暗想终于理我了,于是跪地求饶,说着姑奶奶绕了我吧,你要我干什么我都答应你,于是小美想着,这臭小子不知到哪回就越界了,于是就说这样吧,我画一条线,你敢越过这条线,就等着挨打吧!小帅嘴上说着,我一定不会再越了,可心里怎么想的,没人知道……

在这里插入图片描述

 故事的重点在于——划线的操作。

  • 划线的本质就是区域划分。
  • 这样做的好处就是防止越界,使空间分配合理化。
  • 当访问的范围不在自己的区域时,直接进行报错,在自己的区域时,就正常运行,这正是小美与小帅的故事背后的逻辑。
  • 进程地址空间的代码区,常量区,数据区,这三个区域的划线操作是在编译期间就确定的。,也就意味着编译时就进行划线。
  • 堆区,栈区,其边界是动态变化的,随着运行时变量的开辟与销毁而变化。意味着边运行边划线。

那在操作系统中是如何划线的呢?

struct mm_struct 
{
	//....
	unsigned long total_vm, locked_vm, shared_vm, exec_vm;
	unsigned long stack_vm, reserved_vm, def_flags, nr_ptes;
	
	unsigned long start_code, end_code, start_data, end_data;     
	/*维护代码区和数据区的字段*/
	
	unsigned long start_brk, brk, start_stack;       
	/*维护堆区和栈区的字段*/
	
	unsigned long arg_start, arg_end, env_start, env_end;  
	/*命令行参数的起始地址和尾地址,环境变量的起始地址和尾地址*/
	//....
};

说明:

  1. 进程地址空间本质上就是一个数据结构对象。
  2. 那操作系统如何对对象进行管理呢?当然是先描述,再组织,最后形成与PCB类似的mm_struct对进程地址空间进行管理。
  1. 虚拟地址的分布是规律的,那物理地址呢?
  • 首先虚拟地址与物理地址通过页表的映射进行联系起来。
  • 其次从进程的角度来看,虚拟地址已经是很便于进行管理。
  • 而且只需管理虚拟地址,物理地址也被间接的被有规律的管理了起来。
  • 因此物理地址没必要再是有序的。
  • 总的来看可以认为物理地址是乱序的存储,有序的被间接管理
  1. 从操作系统的角度看,程序最终如何管理变量,是变量名吗?
  • 首先程序代码由编译器与链接器处理之后,只剩下了二进制指令,不存在变量名。
  • 其次从内存的角度看,获取到了地址,即获取到了数据,因此在底层是通过不断更改地址来获取数据的。

3.3 进程地址空间

  1. 进程相互之间是如何做到独立的?

我们再来讲一个故事:

  • 从前有一个大富翁,身价十个亿,而且到处沾花惹草,但是却让3个女人,心甘情愿地为他生了三个儿子,可见"超"能力是多么的有魅力。

  • 由于这位大富翁是一位时间管理大师,对三个儿子照顾的很好,竟然让这三个儿子都不知道彼此之间的存在。

  • 随着大富翁一天一天的老去,也开始思考如何分配遗产,由于对三个儿子都很疼爱,于是做个一个这样的决定:分别对三个儿子说:儿子," 爹老了,等爹去世了,这十亿资产就是你的了。"

  • 因为都认为父亲只有我这一个儿子,因此三个儿子都对此深信不疑。

  • 于是有一天儿子甲经济困难,来找大富翁要20万美金,大富翁二话没说,直接往儿子甲卡里打了30万美金,儿子甲屁颠屁颠的走了;

  • 儿子乙因为快要上大学了,于是问大富翁要了10万美金,大富翁也二话没说,直接往儿子乙身上打了20万。

  • 儿子丙因为一事无成,于是整天想着继承大富翁的十亿家产,于是选择一天,鼓足勇气的给大富豪说,“爹,我现在就想继承十亿家产”,大富翁很是生气,于是就怼过去,“你爹还没死呢!就想着继承家产了。” 就直接让儿子丙滚了,儿子丙也觉得没有什么不对,就走了。
    ……

  • 故事到这里就讲完了,回归到操作系统,这里的大富翁的十亿资产对应的是一整块物理内存,大富翁对应的是操作系统,而这儿子对应是进程

  • 进程在申请内存时,本质上是操作系统申请的,由操作系统决定是否分配给进程空间,而进程与进程在申请内存之间没有联系。

  • 其次每个进程有属于自己的进程地址空间,也就意味着有着各自的虚拟地址。

  • 而物理地址实际上是由操作系统进行分配与管理的,两者之间没有联系。

  • 但是由于页表的存在,建立了映射关系,可以将虚拟地址与物理地址联系起来。

  • 但进程与操作系统还是各管各的,只不过通过页表的修改而将虚拟地址与物理地址统一起来。

问题:进程为什么不直接在内存上使用物理地址,而非得通过虚拟地址进而使用物理地址?
解释:

  1. 虚拟地址也是地址,在没有通过页表映射的物理地址之前,操作系统可以进行一层检查,从而过滤掉非法的申请信息,就比如在32位的机器下,申请4GB的内存。
  2. 内存只有一块,也就意味着物理地址是唯一的,如果直接使用物理内存,也就意味着将内存地址暴露给了用户,一旦用野指针修改其中的未知区域,报错可能会导致整个系统的瘫痪!
  3. 结构相同的进程地址空间,即虚拟地址存放的位置,意味着可泛化,统一的管理,从而便于进程对进程地址空间的管理。
  4. 一般我们将进程的操作称作进程管理模块,而操作系统的管理内存的模块称作内存模块,两者通过页表和进程地址空间,即物理地址与虚拟地址进行解耦合,更加的独立。

3.4 再识页表

问题1:页表如何判断虚拟地址非法?
解释:

  1. 页表记录的信息不只有虚拟地址与物理地址的映射,还记录着虚拟地址的权限。
  2. 比如当修改常量区的代码时,这时页表通过其权限判断只有可读权限,由此直接报错,不进行修改。

问题2: 页表的地址是虚拟地址还是物理地址?页表的地址存在哪?
解释:

  1. 不可能是虚拟地址,因为页表存的是虚拟地址与物理地址的映射,如果其还为物理地址,相当与自己存自己的虚拟地址与物理地址的映射,就套娃了,其次也没必要进行存储。
  2. 进程加载到CPU上,页表在进行加载地址时,会将其地址放在cr3寄存器中进行加载,放的肯定是物理地址,在进程运行完毕时,会将其地址放在进程的上下文进行带走,以便于下一次的恢复。
缺页中断

在这里插入图片描述

 这就要再从父进程拷贝子进程具体过程再来理解了。

  1. 子进程在拷贝父进程的时,会共享数据和代码。
  2. 子进程在拷贝父进程页表时,栈区的地址在拷贝过程中其权限会发生转变,由写权限变为读权限。
  3. 在对子进程栈区的数据进行修改时,由于是读权限且在栈区,这时操作系统会再帮子进程申请一块空间,并将其物理地址修改成申请的物理地址再将子进程页表相应的权限改为写权限。


总的来看,在发生页表的当前权限与实际权限发生冲突时,会触发缺页中断。

进程挂起
  • 在Linux中我们只看见过R,S,D,T状态,那挂起状态我们并没见过,那有没有呢?
  • 从定义的角度来看,所谓进程挂起,也就是进程的代码和数据,不在内存而在磁盘当中。
  • 那如何判断数据是否在内存呢?
  • 其实很简单,在页表上打一个标记,比如1表示在,0表示不在。

举个例子:当你启动一个大型游戏,比如说原神。

  1. 在原神的启动时,先生成其进程PCB,进程地址空间,页表。
  2. 在加载代码和数据的过程中,我们的内存并不可能加载所有的数据,这样内存会爆的。
  3. 因此内存采用一种惰性加载的方式,即用什么加载什么。
  4. 因此在用的时候,查看页表其虚拟地址对应的物理地址是否在内存中,如果不在,操作系统就会把相应的数据和代码加载到内存中,然后再进行执行。

总的来看:这里的页表的本该存在实际不存在发生冲突,从而触发了缺页中断。

总结

问题1:我们再来看一看进程是由什么组成的?
解释:在原先的PCB数据结构对象 + 代码和数据的基础上,再加上了进程的上下文,进程地址空间(mm_struct),页表。

  • 初步认识了进程地址空间与页表,并对程序的底层有了更深的了解。

 今天的分享就到这里了,如果感到有所帮助,不妨点个赞鼓励一下吧!

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

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

相关文章

2010-2021年北大中国商业银行数字化转型指数数据(第三期)

2010-2021年北大中国商业银行数字化转型指数数据(第三期) 1、时间:2010-2021年 2、指标:银行名称、银行类型、年份、战略数字化、业务数字化、管理数字化、数字化总指数 3、来源:北大数字金融研究中心 4、数据说明…

中文大语言和多模态模型测评

Notion – The all-in-one workspace for your notes, tasks, wikis, and databases.A new tool that blends your everyday work apps into one. Its the all-in-one workspace for you and your teamhttps://yaofu.notion.site/C-Eval-6b79edd91b454e3d8ea41c59ea2af873排行榜…

Appium移动端自动测试框架,如何入门?

Appium是一个开源跨平台移动应用自动化测试框架。 既然只是想学习下Appium如何入门,那么我们就直奔主题。文章结构如下: 1、为什么要使用Appium? 2、如何搭建Appium工具环境?(超详细) 3、通过demo演示Appium的使用 4、Appium如何…

【Unity程序技巧】异步保险箱管理器

👨‍💻个人主页:元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 收录于专栏:Uni…

Servlet的两种部署方法

Servlet是实现动态页面的技术,是tomcat给Java提供的原生的进行web开发的api 第一个Servlet程序 写一个servlet程序,部署到tomcat上,通过浏览器访问,得到hello world字符串 1.创建项目 此处要创建的是maven项目 maven&#xf…

重磅官宣 | 第二届 OpenHarmony 技术峰会,邀您共启智联未来

"下一个技术未来在哪里?" 11 月 4 日 技术大咖齐聚北京为你解答 一场主论坛八大开源领域分论坛 探究终端操作系统十大技术挑战方向 与全球开源操作系统技术领袖、实践专家、一线导师携手 共绘 OpenHarmony 开源生态璀璨星图! 点击链接&…

解决:无法打开Zotero数据库

在实验室电脑上面下载了Zotero和坚果云,一系列操作下来,我的笔记本上无法打开Zotero数据库了!显示下面的界面: 于是网上找解决方法 1.https://www.zhihu.com/question/519740718 2.https://devpress.csdn.net/awstech/64e7311b…

Vue ref属性

Vue中的ref属性可以用来对HTML元素或者是对组件进行唯一标识。 一、设置ref属性 只需要在元素或者是组件后跟上如下语法即可: ref"标识名" 二、获取元素或对象 我们可以用如下方法获取我们设置ref的元素或组件: this.$refs.标识名 第一个输…

对批改网禁止复制粘贴问题的破解

首先进入到作文页面 右击鼠标显示弹窗选择检查 点击左上角图标 之后鼠标点击作文框,检查框会跳转到文本 点击鼠标右键选择文本,并选择编辑为HTML 在文本内写入内容点击空白处退出即可

JAVA设计模式全解(独家AI解析)

JAVA设计模式全解(独家AI解析) 一、JAVA介绍二、JAVA设计模式六大原则三、JAVA设计模式介绍四、JAVA设计模式详解4.1 单例模式4.1.1 懒汉式(Lazy Initialization)4.1.2 饿汉式(Lazy Initialization) 4.2 代…

Java中获取异常栈中的底层异常信息-分析Java异常栈

Java中获取异常栈中的底层异常信息-分析Java异常栈 首先,我们准备好一个多层异常栈堆叠的示例代码: public class ExceptionUtils {public static void main(String[] args) {try {buildMultiLayerExceptionStack();} catch (Exception e) {e.printSt…

RHCE---shell 条件测试

文章目录 目录 文章目录 前言 一.条件测试 概述: 文件测试 整数测试: 总结 前言 当我们完成某一命令的编写时,除了观察输出的内容,我们又如何得知命令是否执行成功呢? 这里,我们需要用到条件测试 一.条…

SEO内链优化的8个终极策略

网站SEO优化主要分为两个大类,一类是站外优化,另一类是站内优化。站内优化的内链建设优化是SEO优化工作的重中之重,可以视为网站内部的内功修炼之一。本文将介绍什么是内链以及如何通过内链优化来提升SEO排名。 什么是内链? 内链…

ArrayList与List的层级关系及ArrayList解析

List与ArrayList的关系 List List是一个接口,不能直接实例化。如果要使用必须去实例化List的实现类——ArrayList和LinkedList站在数据结构的角度看,List就是一个线性表。常见的线性表:顺序表、链表、栈、队列等 线性表 线性表是n个具有相…

解决SpringBoot整合Activiti引用JPA:缺少javax.persistence.EntityManagerFactory

系统接入工作流Activiti的时候,发现activiti初始化表,操作数据库使用的是jpa,我们这里解决办法就是引入spring-data-jpa-starter自动配置EntityManagerFactory。 首先把工作流sql文件执行 基础依赖jar引入 这里忽略Activiti的基础依赖,以下…

什么是云原生?土生土长?

“云原生”(Cloud Native)是一种构建和运行应用程序的方法,这种方法充分利用了云计算的优势。云原生应用程序是为云环境设计的,通常是在容器中运行,并被设计为在微服务架构中运行,这使得它们能够快速扩展和…

进公司第二天:绿盾+TFS拉取代码

1.配置绿盾 绿盾:一种安全软件 遇到的问题 TFS端口号:192.168.3.231 服务端口号:默认(千万别瞎写啥的) 2.配置TFS拉取代码 TFS利用插件 ,输入公司给你的信息你的各种信息 拉取代码就行。 3.查看代码 —…

系统架构师备考倒计时12天(每日知识点)

1. 基于架构的软件设计(ABSD) (ABSD方法是架构驱动,即强调由业务【商业】、质量和功能需求的组合驱动架构设计。ABSD方法有三个基础。第一个基础是功能的分解。在功能分解中,ABSD方法使用已有的基于模块的内聚和耦合技术;第二个基…

上位机通过Modbus转Profinet网关与变频器Modbus通讯配置案例

上位机与变频器Modbus通讯是通过Modbus转Profinet网关来实现的。这个网关可以理解为一个通信翻译器,负责将上位机通过Modbus协议发送的数据转换为Profinet协议,再通过Profinet网络与变频器进行通信。 上位机通过Modbus转Profinet网关与变频器Modbus通讯&…

一些基本图像预处理---将一个文件中的所有图片进行尺度归一化、查看每个像素的RGB值,将指定颜色切换为白色、将文件中所有图片从png到jpg的切换

目录 1.将一个文件中的所有图片进行尺度归一化 2. 查看一张图片的每个像素的RGB值 3.将指定颜色切换成其他颜色(代码中将红色切换为白色) 4.将一个文件中所有图片从png到jpg的切换 5.将一个文件夹中的所有图像重命名从1开始 1.将一个文件中的所有图…