0


【Linux】理解进程地址空间

🍎作者:阿润菜菜
📖专栏:Linux系统编程

​我们在学习C语言的时候,都学过内存区域的划分如栈、堆、代码区、数据区这些。但我们其实并不真正理解内存 — 我们之前一直说的内存是物理上的内存吗?

前言

我们先看一段测试代码:

#include<stdio.h>#include<assert.h>#include<unistd.h>int g_value =100;//全局变量intmain(){// fork在返回的时候,父子都有了,return两次,id是不是pid_t类型定义的变量呢?返回的本质,就是写入!// 谁先返回,谁就让OS发生写时拷贝pid_t id =fork();assert(id >=0);if(id ==0){//childwhile(1){printf("我是子进程, 我的id是: %d, 我的父进程是: %d, g_value: %d, &g_value : %p\n",\
                    getpid(),getppid(), g_value,&g_value);sleep(1);
            g_value=200;// 只有子进程会进行修改}}else{//fatherwhile(1){printf("我是父进程, 我的id是: %d, 我的父进程是: %d, g_value: %d, &g_value : %p\n",\
                    getpid(),getppid(), g_value,&g_value);sleep(1);}}}

运行结果:在这里插入图片描述
我们可以注意到子进程的变量值内容发生了改变,而父进程的变量值内容一直没有发生改变,并且两个进程的全局变量打印出来的地址值是一样的。
那这地址到底是不是真的物理地址
== 当然不能是,如果是同一个物理地址,不可能读取同一个变量会读取到不同的数值 。==

从上面结果我们可以得出: 子进程对全局变量数据修改,不影响父进程 - — 进程具有独立性 也就是说,我们在语言层面用的地址,不是物理地址

所以我们之前说‘程序的地址空间’是不准确的,那准确的说这是什么地址呢?
我们一般叫:虚拟地址或者线性地址
我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理。OS必须负责将 虚拟地址 转化成 物理地址 。

理解进程地址空间

通过故事引入

我们知道操作系统会帮助我们用于管理计算机硬件和软件资源.假设操作系统是个大富翁,手底下有10个亿的内存,大富翁同时有四个私生子,四个分别从事不同的活动,彼此不知道互相的存在(对应进程的独立性),在大富翁老去时,会将自己的财产继承给私生子,那么由于每个私生子彼此不知道存在,大富翁让每个私生子都会以为自己可以继承10个亿的财产(画的大饼),是孩子就总会有给老爹要钱的时候,那么为了管理自己的10个亿财产,大富翁很有必要将其描述组织起来,以供自己四个彼此独立的私生子使用。
那么大富翁(操作系统)将画的大饼先描述,在组织,其实就是管理进程地址空间的过程 ---- 本质就是:一个内核数据结构,struct mm_struct{ }
在这里插入图片描述
现在我们知道了地址空间就是内核数据结构 ---- 那它是怎么对应物理内存的?
先来看一下地址空间是怎么划分的:

代码区、数据区、堆区等这些区域如何理解?

在我们小学的时候可能会遇到课桌上有一道“线”的情况,是什么线?三八线。那当时画三八线的本质就是:区域划分 — 地址空间就是线性区域
同样在Linux内核数据结构中也存在区域划分,类似下面的这种代码,用来管理内存空间:

structarea{int start;int end;}

同时,我们对线性区域进行指定start和end即可完成区域划分 ---- 类似于这样

struct area  owner_1 (1,50};
struct area  owner_2 (50,100};

如果限定了区域,那区域之间的数据是什么?以一个4GB的内存为例,大概是这样的:

structmm_struct//4GB{long code_start;long code_end;long init_start;long init_end;//.....long stack_start;long stack_end;}

在这里插入图片描述
通过上述,我们可以知道地址空间区域是可以进行动态调整大小的 ---- 即更改 start或者end
同时虚拟地址是经过页表(+MMU(集成在cpu中))映射到物理地址 ---- 像是我们大学生会被学号编号,进行确认
在这里插入图片描述
同时根据上述知识可以知道,同一个变量,地址相同,其实是虚拟地址相同,内容不同其实是被映射到了不同的物理地址
找到地址不是目的,而是一种手段(页表),目的是该地址对应的内容!

到这里还能回答原始问题:子进程的mm_struct继承父进程 ---- 即虚拟地址一样 进程独立,映射到不同的物理地址

深入扩展

地址空间为什么要存在?
---- 比如野指针越界问题,破坏了进程独立性,进程的数据会遭到破坏,影响到进程运行

  • 防止地址随意访问,保护物理内存与其他进程运行 - 同时页表具有读写权限控制属性,解释了为什么代码段只是可读的,为什么有些变量不能赋值
  • 进程管理和内存管理进行解耦合 | 通过malloc本质讲解
  • 可以让进程以统一视角看待自己的代码和数据

malloc 本质

作为一款优秀的操作系统,不能允许任何的浪费或者不高效的存在。
所以操作系统使用缺页中断方式来管理内存 ---- 即先在虚拟地址空间申请虚拟内存。
然后通过页表映射 ,在实际物理内存上开辟空间 同时不需要关心数据放在物理内存哪个位置 因为通过页表映射都能找到

请添加图片描述
重新理解地址空间

  1. 我们的程序在被编译的时候,没有被加载到内存,那么我们的程序内部有没有地址呢? 答案是:有的。源代码被编译的时候,就已经按照虚拟地址的方式进行了代码和数据的编址(使用ELF格式:划分数据区域)所以虚拟地址这样的策略不只是影响OS,我们的编译器同样遵守这样的规则!
  2. 进程的代码和数据必须一直在内存中吗? 答:不是。OS会将暂时不用的进程代码、数据和部分进程控制块通过我们的页表技术交换至磁盘中存储。
标签: linux 服务器 运维

本文转载自: https://blog.csdn.net/weixin_62676865/article/details/129252061
版权归原作者 阿润菜菜 所有, 如有侵权,请联系我们删除。

“【Linux】理解进程地址空间”的评论:

还没有评论