【Linux从入门到放弃】进程地址空间

🧑‍💻作者: @情话0.0
📝专栏:《Linux从入门到放弃》
👦个人简介:一名双非编程菜鸟,在这里分享自己的编程学习笔记,欢迎大家的指正与点赞,谢谢!

在这里插入图片描述

进程地址空间

  • 前言
  • 一、地址空间
    • 1.1 地址空间的问题引入
    • 1.2 什么是地址空间?
    • 1.3 地址空间、物理内存的关系
  • 二、地址空间的意义
    • 2.1 防止地址随意访问,保护物理内存与其他进程
    • 2.2 将进程管理和内存管理进行解耦合
    • 2.3 让进程以统一的视角看待自己的代码和数据
    • 扩展1 malloc的本质
    • 扩展2 重新理解地址空间
  • 总结


前言

  在之前的学习当中,我们肯定关于数据的存储有一定的了解,比如全局变量应该存储在什么位置,动态申请的空间又在什么位置等等。此篇博客就关于在Linux环境中对地址空间作以详细讲解。


下图我们之前学习C语言应该了解的空间布局图,那么有一个问题:这块空间是不是我们所说的内存呢?带着这个问题进入到此篇博客中。
在这里插入图片描述

一、地址空间

1.1 地址空间的问题引入

  关于进程地址空间的学习我们先看一段代码:有一个全局变量num,通过fork创建子进程,在父子进程打印全局变量以及它的地址,在父进程里5秒之后对这个全局变量进行修改。

int num=10;
int main()
{
	int id=fork();
	assert(id>=0);
  	if(id==0)
  	{
    	//子进程
    	while(1)
    	{
      	printf("i am child,num:%d,&num:%p\n",num,&num);
      	sleep(1);
    	}
  	}
  	else
  	{
  		//父进程
    	int cnt=5;
    	while(1)
    	{
      		printf("i am father,num:%d,&num:%p\n",num,&num);
      		if(--cnt==0)
        		num=20;
      		sleep(1);
    	}
  	}
 	return 0;
}

在这里插入图片描述

父进程对全局数据做修改,并不会影响子进程,这是因为进程具有独立性,而进程的概念是内核数据结构+代码和数据,其实在数据修改的时候发生了写时拷贝。

  通过这段代码的运行结果可以发现:五秒之前,父子进程的num值都是10,当然对应的数据地址也是一样的,但是在5秒之后,父进程的num值发生了改变,可是奇怪的是num对应的地址怎么还是一样的,怎么在同一份地址空间里存放着不同的数据呢?这些显然不可能。
  这也就说明了这里打印出来的地址并不是物理地址,它的名字而是虚拟地址或者线性地址。

1.2 什么是地址空间?

  先讲个小故事,从前有一个大富豪,可谓是富甲一方,假设说有RMB十个亿,一个人有了钱就容易花天酒地,于是就有了好几个私生子,但是这个孩子都是生活在不同的地方,彼此并不知道对方的存在。这个富豪就给这几个孩子说等我到时候没了,我的这些资产都是你的。如果这几个孩子给他老爸要上个几千几万,这个富豪肯定就毫不犹豫地给了,但要是说:“老爸,给我五个亿,我想自己创业办个公司”,这种情况富豪肯定是不会将钱给他孩子的。
  可以这么说,这个富豪给他每个孩子的承诺将相当于是在画饼,而这个饼就是进程地址空间,大富豪是操作系统,资产是内存,那这几个私生子是进程

对于大富豪来说,他给每个孩子画着不同的饼,那么他就应该将这些“饼”管理起来,以便以后更好的去给孩子画饼。而管理的方式就是先描述,再组织。
而饼的本质就是一个内核数据结构,是一个struct mm_struct{}这样的一个结构体。
每当创建了一个进程,相应也会有自己的独一份内核数据结构,进程就会通过一个指针指向这块结构体,对于我们来说看到的“内存地址”其实是这个进程地址空间的某一位的地址—虚拟地址。

  1. 说到三八线我想每一个人都不陌生,可能我们每个人小的时候都和同桌在课桌上画过这样的线,我们当时画三八线的初衷肯定就是限定对方的活动区域,其本质就是区域划分。那在计算机里对一线性区域进行划分指定好区域空间的起始处和终止处即可完成区域的划分。
  2. 地址空间的本质就是线性区域,我们考虑在32位计算机环境下,地址空间的地址大小是从全0到全F(16进制)的,整个地址空间是以一个字节为最小单位划分的。 之所以称这段空间为线性区域是因为地址是线性的,若是十进制的话就是从0到42亿多。

如果说我们定义一个int类型的数据,在地址空间就会占四个字节,按道理来说我们应该有四个地址,但是当对这个变量取地址时就会发现拿到的只是这段空间的起始地址,那仅仅通过这一个地址怎能将本来四个字节所存放的数据拿到呢?其实除了起始地址之外,还有就是这个数据的类型起到关键的作用。

  1. 从最上面的图可以看到,地址空间被划分成了一个个小的区域,这些区域的起始地址和终止地址会存放到mm_struct结构体中,通过这一对一对的地址将地址空间进行了划分。
  2. 如果通过一对对的地址限定了区域,那么这段区域中间存放的是什么东西呢?虚拟地址
  3. 区域的大小调整的本质就是对起始地址和终止地址大小的调整。

1.3 地址空间、物理内存的关系

  我们所看到的地址其实是虚拟地址,而真正的数据是存放在物理内存中的,既然我们能够从这个虚拟地址拿到我们所需要的数据,那就说明了在这两者之间肯定存在这一种映射关系,能够将虚拟地址转化为物理地址,从而拿到所需要的数据,这种映射关系存放在一个叫页表的空间当中,而虚拟地址和物理地址的映射关系相当于K-V关系。
在这里插入图片描述

通过地址空间中的虚拟地址找到页表对应的位置,然后根据页表的映射关系获取到物理地址,从而拿到想要的数据。

  接下来再对那段代码所遗留下来的问题作以解释:每创建一个进程,操作系统都会为每个进程创建所对应的地址空间和页表,而在fork之后,同样会和父进程一样创建内容一样的地址空间和页表,也就是相同的地址和相同的数据,如下图。
在这里插入图片描述
  然而当父进程对全局变量修改之后,就会发生写时拷贝,是怎么回事呢?

  1. 因为进程之间具有独立性,父进程修改数据肯定是不会影响到子进程的,当父进程修改数据就会在物理内存中重新找一块空间将改变后的数据存放其中,然后父进程对应的页表的映射关系就会发生改变。
  2. 当然虚拟地址不会发生变化,变化的只是改变后的数据的物理地址需要在页表中重新映射。
    在这里插入图片描述

从这幅图就能明白为什么相同的地址对应的数据竟然不一样,原因就是我们所看到的地址并不是真正存放数据的地址,是将这个我们所看到虚拟地址经过父子进程不同的页表映射拿到了不同的物理地址,进而获取的数据不同。

  1. 同样,在父子进程的返回值当中也发生了写时拷贝。fork在返回时,父子进程都被创建出来了,而fork函数会return两次,因此表现出了会有两个执行流。而返回的本质就是写入,谁先返回,谁就让操作系统发生写时拷贝。

二、地址空间的意义

2.1 防止地址随意访问,保护物理内存与其他进程

如果没有地址空间的存在,进程就会直接与物理内存打交道,假设磁盘中自己写的A、B代码拿到了物理内存中,那么当执行A代码时因为代码的问题而产生了野指针问题,恰好会对B代码产生影响,显然这样是不安全的,而正是因为地址空间的存在,在与物理内存沟通时得先经过页表的映射,如果有野指针问题或者其他一些会影响其他进程的问题操作系统就会直接终止你的行为,保证你不会影响到其他进程。

举个例子: 一个小孩子过年领了好多压岁钱,但是他对于钱的概念并不是头脑很清晰,有一个良心不好的商家看他是一个孩子,故意高价卖一些东西或者骗这个孩子买了一些他根本不需要的东西,最后他父母知道这件事后认为孩子拿钱不行,于是就给他孩子说:”你的压岁钱妈妈替你保管,你想要买啥你给我妈妈要“,但这个孩子说我要买棒棒糖,那么他妈妈就给了他两块钱去买,但是当他说我想要两千块买个手机玩一玩,那么家长肯定不同意。

在没有家长替孩子保管钱之前就相当于没有地址空间的存在,孩子就可能会乱花钱,而家长的存在就起到了保护的作用。

  1. 除此之外,通过页表进行地址转换的时候从CPU拿到的不仅仅是虚拟地址,还包括这次操作是读还是写。这样就可以在页表转换前在mm_struct中进行操作验证,假设你要访问的是代码区的地址,但是发现其配套的地址是栈区,所以操作系统就会直接拦截你的操作。
  2. 如果在mm_struct这里的虚拟地址是正确的,在页表里的除了进行地址转换,页表中还包含一项为权限标志位,表示该数据是读还是写。
    char *str="hello"; *str='H';这两行的代码目的是想将str对应字符串的’h’改成’H’,但是这样的做法是不可以的,原因就在于通过指针访问的时候会有权限的约束,只读而不可写。

2.2 将进程管理和内存管理进行解耦合

先看扩展1

  1. 在整个地址空间和物理内存的框架中,以页表中间为分隔线,以地址空间为主的为进程管理,以物理内存为主的为内存管理。
  2. 因为有地址空间以及页表的存在,所以进程是不会关心访问物理内存的哪个位置。也就是说无论数据被保存在物理内存哪个位置都是可以的。作为进程来说,我只需要知道虚拟地址即可,具体数据在物理内存哪个位置我不关心,通过页表的地址转换我都是可以在物理内存中找到的。
  3. 如果说没有地址空间,那么数据每次被加载到物理空间的地址会发生改变。因为有了地址空间和页表,无论物理内存的地址怎么变,变得只是页表的映射关系,进程能拿到得地址是不会发生变化的。

2.3 让进程以统一的视角看待自己的代码和数据

先看扩展2

  1. 进程的代码和数据被编译后被加载到物理内存时,每条指令的内容都包含着下一条指令对应的虚拟地址,每当CPU执行完一条指令后都会拿到下条指令的虚拟地址,通过虚拟地址的转换找到对应的物理地址,拿到对应的指令在CPU执行,周而复始。
  2. 可以看出来每次CPU拿到的都是虚拟地址,保证进程每次都通过地址空间来执行下一步的任务。

扩展1 malloc的本质

 通过malloc这样的方式动态申请空间,操作系统会在申请之后立马在物理内存中开辟一段空间给你使用吗?

  1. 在你申请了一段空间之后,站在计算机的角度可以认为你是无法做到立马使用的。如果说在申请之后立马在物理内存中开辟出相应的空间那么必然会导致在你使用这段空间之前会有一段时间窗口,在这段时间里这段空间是无法被别的进程使用,处于闲置状态。
  2. 但是在操作系统的世界里,它是不允许不高效的使用和任何浪费,因此物理内存不会在你申请的时候而是在你需要的时候给你。
  3. 作为操作系统,它真实的做法是在你申请了空间之后,会在地址空间开辟出相应大小的空间,然后再将这块空间的首地址存放在页表的相应位置处,但是其对应的物理地址此时并不在页表中给出,操作系统会在你需要的时候在物理内存中开辟相应的空间,再将对应的物理地址填在页表中。

扩展2 重新理解地址空间

 我们的程序在被编译的时候,没有被加载到内存,那么我们的程序内部有没有地址呢?

答案是有的。

  1. 源代码被编译的时候,就是按照虚拟地址空间的方式对代码和数据早就已经编好了对应的编址。
  2. 当程序被编译后还没有被加载到内存,它会被放在磁盘中。假设有两条指令,其中一条为call指令(call 0x1122),另一条为被call指令( 0x1122 xxxx)。先将这两条指令加载到内存中,之后进程通过虚拟地址、页表转换拿到call指令对应的物理地址的内容至CPU执行,此时进程就拿到了地址0x1122,这个地址是虚拟地址,再次通过页表转换拿到被call指令的物理地址处的内容(xxxx)再去执行。
  3. 每次CPU读到的地址都是虚拟地址。

总结

  虚拟地址空间是一种在操作系统内部为进程创建出来的一种具体的数据结构对象,用来让进程统一的视角看待物理内存,因为有了虚拟地址空间的存在,可以让内存管理和进程管理独立开来,并且因为有地址空间的存在,我们在磁盘中编译程序时,就可以把编译好的程序以地址空间的方式排布好,这样当程序加载到内存时,CPU在进行读取识别时拿到的地址都是虚拟地址,再通过页表地址转换找到对应程序并且拿到下条指令的虚拟地址。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

网络安全管理组织架构复习

文章目录 安全管理机构岗位设置安全要求要求解读 安全管理机构 安全管理的重要实施条件就是有一个统一指挥、协调有序、组织有力的安全管理机构,这是网络安全管理得以实施、推广的基础。 通过构建从单位最高管理层到执行层及具体业务运营层的组织体系,可以明确各个…

示例:WPF中DataGrid设置多级分组样式

一、目的&#xff1a;应用CollectionViewSource和GroupStyle设置DataGrid多级分组样式 二、实现 一级分组效果如下 二级分组效果如下 三、环境 VS2022 四、示例 具体实现代码如下 <TabItem Header"DataGrid - 多级分组"><TabItem.Resources><Colle…

最新源支付系统源码 V7版全开源 免授权 附搭建教程

简介&#xff1a; 最新源支付系统源码_V7版全开源_免授权_附详细搭建教程_站长亲测 YPay是专为个人站长打造的聚合免签系统&#xff0c;拥有卓越的性能和丰富的功能。它采用全新轻量化的界面UI&#xff0c;让您能更方便快捷地解决知识付费和运营赞助的难题。同时&#xff0c;…

thread model线程模型

Concurrency并发 vs. Parallelism执行 Many-to-One GNU Portable Threads 多对一即多个用户线程映射到单个内核线程 该模式用的少 一个线程阻塞会导致所有线程阻塞 多线程可能无法在多核系统上并行运行&#xff0c;因为同一时间内核中可能只有一个线程 Concurrent executio…

看热点新闻从中获取的客观信息有哪些?

提取热点新闻的客观价值&#xff1b; 避免主观情绪的投射代入。 参考网址&#xff1a; https://damo.alibaba.com/alibaba-global-mathematics-competition?langzh&languagezhhttps://damo.alibaba.com/events/32024060617176459249878092?languagezh 这份名单&#x…

【ARMv8/v9 GIC 系列 3 -- GIC 的 类型寄存器 GICD_TYPER】

文章目录 GIC 类型寄存器 GICD_TYPERESPI_Range, 位[31:27]RSS, 位[26]No1N, 位[25]A3V, 位[24]IDBits, 位[23:19]DVIS, 位[18]LPIs, 位[17]MBIS, 位[16]NUM_LPIs, 位[15:11]SecurityExtn, 位[10]NMI, 位[9]ESPI, 位[8]CPUNumber, 位[7:5]ITLinesNumber, 位[4:0]GIC 类型寄存器…

无线串口模块有什么优势特点及串口参数设置

无线串口是一种无线通信技术&#xff0c;它允许原本通过有线连接&#xff08;如RS232、RS485等标准串行接口&#xff09;的设备之间通过无线方式进行数据传输。无线串口模块通常包括发射端和接收端两部分&#xff0c;发射端将串口数据转换为无线信号并发射出去&#xff0c;接收…

动态规划——达拉崩吧

1、题目链接 174. 地下城游戏 2、题目分析 假如说我们正向推状态转移方程&#xff0c;很难推出来&#xff0c;因为这道题有“加血”的说法&#xff0c;只能依靠后面的值判断前面所需要的血量&#xff0c;也就是说&#xff0c;如果正向的dp表示从起点出发&#xff0c;到达&…

【ajax基础05】利用ajax渲染数据思路总结

目录 一&#xff1a;利用字符串渲染 二&#xff1a;获取标签进行数据渲染 1 前置知识点 2 从服务器获取数据为对象 核心思想&#xff1a; 关键&#xff1a; 进行数据渲染&#xff0c;无非就两个步骤1 从服务器获取到数据2 将数据渲染到html结构当中 因此不同的渲染思路…

119.网络游戏逆向分析与漏洞攻防-邮件系统数据分析-邮件读取与删除功能的封装

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果&#xff0c;代码看不懂是正常的&#xff0c;只要会抄就行&#xff0c;抄着抄着就能懂了 内容…

vivado TILE

TILE是包含一个或多个SITE对象的设备对象。可编程逻辑TILE 包括各种各样的对象&#xff0c;如SLICE/CLB、BRAM、DSP、I/O块、时钟资源&#xff0c;以及 GT块。从结构上讲&#xff0c;每个瓦片都有许多输入和输出&#xff0c;并且可编程 互连以将瓦片的输入和输出连接到任何其他…

她经济和女性经济,女性消费力量的崛起

在当今这个数字化飞速发展的时代&#xff0c;"她经济"已经不再是一个简单的概念&#xff0c;而是一场正在上演的女性消费革命。 在最新的《QuestMobile 2024“她经济”洞察》报告中&#xff0c;为我们揭示了女性在移动互联网时代的独特地位和影响力。 首先&#xf…

LeetCode 1164, 125, 94

目录 1164. 指定日期的产品价格题目链接表要求知识点思路代码 125. 验证回文串题目链接标签简单版思路代码 复杂版思路代码 94. 二叉树的中序遍历题目链接标签递归思路代码 迭代思路代码 1164. 指定日期的产品价格 题目链接 1164. 指定日期的产品价格 表 表Products的字段为…

HAC-TextRank算法进行关键语句提取

AI应用开发相关目录 本专栏包括AI应用开发相关内容分享&#xff0c;包括不限于AI算法部署实施细节、AI应用后端分析服务相关概念及开发技巧、AI应用后端应用服务相关概念及开发技巧、AI应用前端实现路径及开发技巧 适用于具备一定算法及Python使用基础的人群 AI应用开发流程概…

黑马头条Minio报错non-xml response from server错误的解决方法

今天在写项目的时候&#xff0c;想测试minio上传文件功能是否正常&#xff0c; 但是每次都出现non-xml response from server的错误。 自己也在网上找了很多解决方法&#xff0c;大部分是说用户名和密码的配置问题&#xff0c;但是检查后发现并没有错误。 最后发现是自己的dock…

AGI|以ChatGPT为例,浅析AI究竟能干什么?

目录 一、前言 二、ChatGPT 三、Prompt Engineering 四、神经网络 五、后记 一、前言 当一个新事物的出现&#xff0c;最好的办法就是了解它出现的背景&#xff0c;发展的历史。 当ChatGPT出现在我们面前&#xff0c;多轮对话能力让人震惊&#xff0c;仿佛机器真的可以&qu…

计算机网络:应用层 - 万维网 HTTP协议

计算机网络&#xff1a;应用层 - 万维网 & HTTP协议 万维网 WWW统一资源定位符 URL 超文本传输协议 HTTP非持续连接持续连接非流水线流水线 代理服务器HTTP报文 万维网 WWW 万维网是一个大规模的、联机式的信息储藏所。万维网用链接的方法能非常方便地从互联网上的一个站点…

linux 安装redis 完整步骤

安装&#xff1a; 1.获取redis资源 复制代码 wget http://download.redis.io/releases/redis-4.0.8.tar.gz 2.解压 复制代码 tar xzvf redis-4.0.8.tar.gz 3.安装 复制代码 cd redis-4.0.8 make cd src make install PREFIX/usr/local/redis 4.移动配置文件到安装…

关于椭圆的方程(有Python画的动图)

关于椭圆的方程&#xff08;有Python画的动图&#xff09; flyfish 几何定义 椭圆是平面上所有到两个固定点&#xff08;焦点&#xff09;的距离之和为常数的点的集合。这两个固定点叫做焦点。 解析几何描述 设椭圆的两个焦点为 F 1 F_1 F1​ 和 F 2 F_2 F2​&#xff…

【数据结构】红黑树实现详解

在本篇博客中&#xff0c;作者将会带领你使用C来实现一棵红黑树&#xff0c;此红黑树的实现是基于二叉搜索树和AVLTree一块来讲的&#xff0c;所以在看本篇博客之前&#xff0c;你可以先看看下面这两篇博客 【C】二叉搜索树-CSDN博客 【数据结构】AVLTree实现详解-CSDN博客 在这…