进程地址空间

一 先前问题解释

  1 #include<stdio.h>2 #include<unistd.h>3 int g_val = 200;4 int main()5 {6     printf("begin");7     int id = fork();8     if(id == 0)9     {10         g_val = 100;11         printf("我是子进程,pid: %d,  父进程:%d,  g_val:%d,  g_val地址:%p\n", getpid(), getppid(), g_val, &g_val);12     }13     else14     {                                                                                                                                                                              15         printf("我是父进程,   pid:%d,  g_val:%d,   g_val地        址:%p\n",getpid(),g_val,&g_val);16     }17     return 0;18 }19 

         核心问题,int id = fork(), id如何即等于0,又大于零,你说发生了写实拷贝,那为什么printf的值不同,地址却相同。

66bdef4002774b50b9f5b9870373ec4c.png

        写时拷贝后地址一样,说明肯定不是物理地址,而是虚拟地址,好吧,虚拟就虚拟喽,那怎么对应真正的物理内存呢?用页表,就像一个表格,左边写着你打印出来的变量的地址,右边写着真正的物理内存地址,cpu只要通过页表就能找到物理内存,所以打印id的地址没变,是因为这是虚拟地址,没必要改,没有意义,操作系统不会做无意义的事,影响效率,只要改父子进程对应的物理地址就行了,所以打印的值不一样,这就是上述代码大致原理。后面了解了页表的结构,理解会更深刻。

但是还有些问题,进程地址空间是什么来的,为什么要弄虚拟地址,后面会一步步解析。

二 进程地址空间的实现

        c语言的程序地址分布,我曾以为下面这个图就是真正的内存,所有的程序都很聪明而且守规矩的往指定地方放数据,例如在代码区放代码,在全局数据区放全局变量。

fdaaba934efd401792ac3327a38bba8a.png

        当我们接触了进程地址空间,我才意识到原来这个不是内存,而是叫进程地址空间,那它是什么呢?什么是地址空间呢?

        由于进程要使用内存,就必然在cpu的运行队列上,那cpu就要去根据地址总线告诉内存要访问的地址,32位机器下cpu和内存相连的线是32根,每根线发射高电频和低电频表示1和0,所有的寻址组合范围都在[0,2^32]内,这个范围的集合就叫地址空间,类似样本空间的概念进程地址空间就是进程能看到的所有内存地址的范围就叫进程地址空间,那是如何实现的呢?先看看其功能,1 管理了所有的内存的地址,这个地址的范围是[0,2^32],2 对内存进行分区域管理,我们从这两个功能来猜猜进程地址空间的实现

        例如一个数组,划分为两个区域,我们希望存数据的时候AB两人最多用一半,那如何保证呢,用下标,让A只能在0到49处存数据,B只能在50,99处存数据,代码上用下标就保证了AB两人各自使用各自的,不互相干扰。而一段空间要分区域管理,实际上用的就是指针,例如stack_begin和stack_end这不就能维护栈的大小了吗,heap_begin和heap_end不就能维护堆了吗(维护区域范围的意义后提)。

        所以操作系统要分区域管理内存,那就要先描述,再组织,也就是用区域指针分开描述各个区域,系统还增加了大小这类参数,防止不同的区域生长的时候碰到一起了,这个不用我们操心。

bce92e1076324044a1654cdf5c7a2450.png

        这个时候我们就可以还原进程地址空间的真面目。

       

        这些begin和end的范围是编译时决定的,因为每个进程的代码大小不同,所以初始的时候,代码段范围不同,而且每一个进程都要有一个进程地址空间,因为独立性,总不能一个进程的栈的范围变了,大家一起变吧,那很容易出现越界误判和数据丢失,所以不能共用一个进程地址空间这个数据结构。不同进程的页表也是独立的,因为进程是根据进程地址空间的begin和end来确定页表的查找范围,所以页表必须是只能用于自己的进程,父子进程的页表是独立的,但我还未看过页表结构,现在将页表理解为表格够用了。

三 虚拟地址空间的意义

        好吧,你先前说的原理实现我都勉强理解,那为什么通过进程地址空间弄出虚拟地址,然后页表转为物理内存地址,有点像中介一样,我不能直接和内存交互吗?当然不能,进程是不可以直接访问硬件的。内存保存数据实际上是通过电,所以对内存充放电就可以保存和删除数据,内存怎么拦得住你对它充电放电。所以必须我们在软件层面上做拦截。

意义1

        所以为了做拦截,增加虚拟地址到物理地址转换,举个例子,当cpu找到页表,通过虚拟地址找物理内存时,操作系统发现你要往代码段写数据,但是这个物理内存是只读的(通过页表的标记判断是只读的),操作系统就可以禁止你的非法行为,那为什么一开始操作系统又可以往代码段写数据,我通过虚拟地址找物理内存写数据又不行呢,我觉得就是因为此时页表标记是只读的,通过这虚拟地址寻址就被拦截了,但是操作系统就可以,我就简单理解为操作系统加载代码不是用的虚拟地址这一套。

意义2 实现了内存管理和进程管理的解耦

        老实说,意义2是我不太理解的。毕竟写的代码不多,我联想到平时写代码的时候,有时候找错误很麻烦,可是有些时候找错误却很轻松,例如我实现了两个函数,fun1和fun2,我需要这两个函数的返回值做下一步运算,现在结果不对,我只要看fun1和fun2的返回值符不符合,我就知道哪里出错了,这一下子就缩小了范围,现在进程管理只需要和页表说我要存数据了,快开物理内存,内存管理就去找了,如果出错了,我们就可以判断大致是哪个模块的问题,修改也只要改这个模块的代码,进程管理则不需要动,我想这就是虚拟地址的意义,让两处代码互不影响,又相互配合。

意义3 统一的视角看内存

        先前说每个进程都有一个进程地址空间,都管理着4个g的内存,也就是说每个进程都认为自己有4个g的内存,你要多少操作系统就申请给你,不够就不给,操作系统不用管这个这个进程的虚拟地址,是不是和其它的进程重复,即便大家的虚拟地址相同,但是有页表,就可以保证映射的物理内存不相同,所有进程都以一个统一视角看待内存,认为这个内存是分区域的,查找数据就可以去特定区域找,而如果没有虚拟地址,直接上物理地址,那操作系统就得保证各个进程的地址不能重复了,维护起来非常麻烦。

   噢,页表拦截非法访问,还让操作系统不用担心虚拟地址是否重复,那进程地址空间好像没啥用,其实进程进程地址空间的作用就是给页表打辅助。你以为虚拟地址是随便写在页表上的吗,是要有根据,有顺序的。

补充:进程地址空间的意义

        如果在页表左侧的地址也是乱序的,那对于进程来说要找一个虚拟地址就要遍历整个页表,而且当堆增大减小的时候,还要删除对应的虚拟地址,这多难找啊,所以页表左侧是有序的非常必要,而不仅要有序还得要知道各个区域的范围,这就是维护进程地址空间的意义,例如有数据要存到堆上,那就在进程地址空间的heap_begin和heap_end范围内选一个虚拟地址写到页表上,然后随便找个申请一个内存出来把地址填到页表上形成映射关系,heap_begin和heap_end随着我们申请资源和释放资源调整范围,左边有序有利于高效完成虚拟地址的查找和删除,快速判断是否是非法访问,右边无序,方便操作系统快速申请,这里我觉得这里设计太巧妙了,我先记录下来,以后再回味回味。

四 页表知识补充

1  缺页中断

        前面说的我大致了解了,但是还有些问题,前面提过挂起状态,linux没有挂起状态,说明无法通过进程状态判断代码和数据在不在内存,那进程是如何知道代码和数据不在内存的呢?还是页表的标记位,例如0表示不在,1表示在,所以当进程要读数据的时候发现数据不在内存,触发缺页中断,操作系统就会让进程等一等,马上加载。

        实际上操作系统对大文件,软件进行惰性加载的方式,例如500mb,只会先加载5mb,因为短期内cpu也跑不完500mb的代码,没必要一次性全加载进来,也是为了不让大软件过多占用内存,这个场景下缺页中断可以大大提高内存的使用率。

        还有就是前面父子进程数据共享,当父进程要修改的时候,也会触发缺页中断,为操作系统发现这个是可修改的变量,却设为进程只读,就知道有其它进程共享,所以就在内存又开了空间保存新数据,并且替换了页表右侧的物理地址。

五 先前知识补充
 

1 什么是进程

        先前说进程是pcb+代码和数据,pcb对象最重要,现在对于什么是进程,更完善的回答是:pcb+进程地址空间+页表+代码和数据,代码和数据仍旧是最不重要的。

2 进程切换切换了什么?

        还有个小细节,cpu上存的是页表的什么地址,如果cpu存的也是页表的虚拟地址,那就要再存一个页表地址,能将页表虚拟地址转为物理地址,那此时这个页表地址又是虚拟地址呢?这就套娃了,所以cpu上寄存器存的的必须是页表的物理地址,从这也可以看出cpu拿到了物理地址,就可以直接访问内存,说明内存硬件根本无法拦截,必须通过软件层,既然是存在寄存器上的,而cpu的寄存器存的数据都属于进程的上下文数据,所以进程切换的时候pcb带走了上下文数据,切换了pcb数据结构,上下文数据没了,页表也切换了,和pcb对应的进程地址空间也自动切换了,这就是目前我对进程切换做的事情的理解。

3 进程独立性如何保证?

        首先PCB是独立的,然后进程地址空间是独立的,页表也是独立的,内存管理保证申请的内存不重复,这样进程代码和数据存在不同的地方,申请和释放也就不会彼此影响了。

往后随着学习的深入,一个进程会有越来越多的数据结构,但是主要框架基本就这些了。

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

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

相关文章

急求!录屏在哪里找?看这里就够了

“电脑的录屏在哪里找呀&#xff1f;已经找了一上午了&#xff0c;真的找不到&#xff0c;有哪位大神知道录屏在哪的&#xff0c;帮帮我&#xff0c;非常感谢&#xff01;” 随着社会的发展&#xff0c;录制电脑屏幕的需求变得越来越普遍。无论是在线教学、游戏直播还是远程会…

【Linux】Linux网络总结图详解

网络是进行分层管理的应用层HTTPHTPPS 传输层&#xff08;UDP、TCP&#xff09;UDPTCPTCP和UDP对比 网络层IP 数据链路层&#xff08;MAC&#xff09;/物理层&#xff08;以太网&#xff09;以太网通信&#xff08;负责网卡之间&#xff09; 网络是进行分层管理的 应用层 HTTP…

HarmonyOS开发:开源一个刷新加载组件

前言 系统Api中提供了下拉刷新组件Refresh&#xff0c;使用起来也是非常的好用&#xff0c;但是风格和日常的开发&#xff0c;有着巨大的出入&#xff0c;效果如下&#xff1a; 显然上面的效果是很难满足我们实际的需求的&#xff0c;奈何也没有提供的属性可以更改&#xff0c;…

nodejs+vue+elementui+python家电销售分析系统设计与实现-计算机毕业设计

系统按照用户的实际需求开发而来&#xff0c;贴近生活。从管理员通过正确的账号的密码进入系统&#xff0c;可以使用相关的系统应用。管理员总体负责整体系统的运行维护&#xff0c;统筹协调。 我们可以利用计算机技术来取代传统的管理模式&#xff0c;实现家电销售分析系统的…

LInux之在同一Tomcat下使用不同的端口号访问不同的项目

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是君易--鑨&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的博客专栏《LInux实战开发》。&#x1f3af;&#x1f3af; …

【RP-RV1126】配置一套简单的板级配置

文章目录 官方配置新建一套新配置新建板级pro-liefyuan-rv1126.mk配置文件新建一个Buildroot的defconfigs文件 吐槽&#xff1a;RP-RV1126 的SDK奇怪的地方make ARCHarm xxx_defconfig 生成的.config文件位置不一样savedefconfig命令直接替换原配置文件坑爹的地方 Buildroot上增…

91 前K个高频元素

前K个高频元素 题解1 大根堆(STL) 给你一个整数数组 nums 和一个整数 k &#xff0c;请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。 示例 1: 输入: nums [1,1,1,2,2,3], k 2 输出: [1,2] 示例 2: 输入: nums [1], k 1 输出: [1] 提示&#xff1a;…

机器人连杆惯量参数辨识(估计)

杆的转动惯量的计算公式是Imr^2。在经典力学中&#xff0c;转动惯量&#xff08;又称质量惯性矩&#xff0c;简称惯矩&#xff09;通常以I 或J表示&#xff0c;SI 单位为 kgm。对于一个质点&#xff0c;I mr&#xff0c;其中 m 是其质量&#xff0c;r 是质点和转轴的垂直距离。…

使用Java与Jsoup库构建有趣的爬虫项目

目录 一、网络爬虫的概念和应用 二、Jsoup库的功能和优势 三、使用Java与Jsoup库编写网络爬虫 四、网络爬虫的法律和道德问题 五、注意事项 六、总结 本文将深入探讨如何使用Java与Jsoup库构建一个实际且有趣的网络爬虫项目。我们将首先简要介绍网络爬虫的概念和应用&…

基于51单片机的智能手机充电器设计

**单片机设计介绍&#xff0c;1660【毕设课设】基于51单片机和MAX1898的智能手机充电器设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 51单片机智能手机充电器设计介绍 51单片机智能手机充电器是一种可以实现智能快速充电的…

华为认证H12-811题库

在VRP平台上&#xff0c;可以通过下面哪种方式返回到上一条历史命令&#xff1f;&#xff08; &#xff09; A、Ctr1U B、Ctr1P C、左光标 D、上光标 试题答案&#xff1a;BD 试题解析&#xff1a;在VRP系统中&#xff0c;ctrlU为自定义快捷键&#xff0c;ct…

Kubernetes 访问控制 - RBAC 鉴权

Author&#xff1a;rab 目录 前言一、Role二、ClusterRole三、RoleBinding四、ClusterRoleBinding总结 前言 API 访问控制有很多&#xff0c;比如 RBAC 鉴权、ABAC 鉴权、Node 鉴权等。自 Kubernetes 1.6 版本以后&#xff0c;RBAC 成为 Kubernetes 的默认访问控制机制。RBAC …

浅谈AcrelEMS-CB商业建筑能源管理系统解决方案-安科瑞 蒋静

1概述 AcrelEMS-CB商业建筑能源管理系统&#xff0c;集电力监控、电能质量监测与治理、电气安全预警、能耗分析、照明控制、新能源使用、能源收费以及设备运维等功能于一体&#xff0c;通过一套系统对商业建筑的能源进行统一监控、统一运维和调度&#xff0c;系统可以通过WEB和…

OpenCV4(C++)——形态学(腐蚀、膨胀)

文章目录 一、腐蚀&#xff08;erode&#xff09;二、膨胀&#xff08;dilate&#xff09;三、形态学操作四、总结 一、腐蚀&#xff08;erode&#xff09; OpenCV 4提供了用于图像腐蚀的erode()函数。 void cv::erode(src, dst, kernel, anchor, iterations, borderType, bo…

自动曝光算法(第一讲)

序言 失业在家无事&#xff0c;想到以后换方向不做自动曝光了&#xff0c;但是自动曝光的工作经验也不能浪费了&#xff0c;准备写一个自动曝光的教学&#xff0c;留给想做自动曝光的小伙伴参考。笔者当时开发自动曝光没有按摄影的avtvevbvsv公式弄&#xff0c;而是按正确的增…

「Java开发指南」如何用MyEclipse搭建Spring MVC应用程序?(一)

本教程将指导开发者如何生成一个可运行的Spring MVC客户应用程序&#xff0c;该应用程序实现域模型的CRUD应用程序模式。在本教程中&#xff0c;您将学习如何&#xff1a; 从数据库表的Scaffold到现有项目部署搭建的应用程序 使用Spring MVC搭建需要MyEclipse Spring或Bling授…

C++使用栈实现简易计算器(支持括号)

使用C实现&#xff0c;使用系统自带stac 支持括号处理支持小数计算支持表达式有效性检查支持多轮输入。 运行结果示例&#xff1a; 代码&#xff1a; #include <iostream> #include <stack> #include <string> using namespace std;//判断是否是数字字符 …

解决pycharm中,远程服务器上文件找不到的问题

一、问题描述 pycharm中&#xff0c;当我们连接到远程服务器上时。编译器中出现报错问题&#xff1a; cant open file /tmp/OV2IRamaar/test.py: [Errno 2] No such file or directory 第二节是原理解释&#xff0c;第三节是解决方法。 二、原理解释 实际上这是由于我们没有设置…

5.5 TCP报文段的首部格式

思维导图&#xff1a; 5.5 TCP报文段的首部格式 基本概念 TCP报文段&#xff1a;包含首部和数据两部分&#xff0c;首部至少20字节。作用&#xff1a;首部字段定义了TCP的功能和行为。长度&#xff1a;首部长度可变&#xff0c;基础首部20字节&#xff0c;可添加选项。 首部…

Openssl数据安全传输平台019:外联接口类的封装以及动态库的制作 - Bug未解决,感觉不是代码的问题

文章目录 1 外联接口1.1 接口类的封装1.2 共享内存与配置文件 2 json格式配置文件的定义2.1 共享内存中存储的节点结构2.2 服务器端配置文件2.3 客户端配置文件2.4 改进配置文件 3 共享内存类修改4 将接口打包成库(静态/动态)4.1 相关的指令4.1.1 静态库4.1.2 动态库 4.2 外联接…