【Linux】进程虚拟地址空间

int main(int argc, char* argv[], char* env[])
{
printf(“code addr : %p\n”, main); // 代码区

printf("\n");
const char \*p = "hello";
printf("read only : %p\n", p);    // 字符常量区(只读)

printf("\n");
printf("global val : %p\n", &g_val);   // 已初始化数据区
printf("global uninit val: %p\n", &g_unval); // 未初始化数据区

printf("\n");
char \*heap_men = (char\*)malloc(10);
char \*heap_men2 = (char\*)malloc(10);
char \*heap_men3 = (char\*)malloc(10);
printf("head addr : %p\n", heap_men);  // 堆区(向上增长)
printf("head addr : %p\n", heap_men2);
printf("head addr : %p\n", heap_men3);

printf("stack addr : %p\n", &heap_men);     // 栈区(向下增长)
printf("stack addr : %p\n", &heap_men2);   //heap\_men本质就是main函数里的指针变量,开辟了空间,所以直接&
printf("stack addr : %p\n", &heap_men3);
int i=0;
for(i=0 ; i < argc; i++)
{
   printf("argv[%d]: %p\n", i, argv[i]);
}
for(i=0;env[i]; i++)
{
   printf("env[%d]: %p\n ", i, env[i]);
} 
return 0;

}


运行结果:


![在这里插入图片描述](https://img-blog.csdnimg.cn/5bd55bba6ce04272af5c264bb620ab22.png)


口诀:**堆,栈相对而生**


#### 🔥语法小问题


1️⃣ 上面的代码,我们malloc了10个字节,可是打印出来的时候


![在这里插入图片描述](https://img-blog.csdnimg.cn/80adb05a61ab4d3db0881fc7bc444b37.png)


却相隔了20个字节,其实是操作系统**多申请了字节**,其中多申请的字节放的是**堆的属性信息(`cookie数据`)**:申请的时间,堆的大小等等


2️⃣ static修饰变量呢?



static int test = 10;
printf(“test stack addr: %p\n”, &test);


![在这里插入图片描述](https://img-blog.csdnimg.cn/6b8d14306880482d962ff2b99120e415.png)  
 🌈static修饰局部变量,本质是把static变量开辟在了**全局区域**


3️⃣在32位下,一个进程的地址空间,**取值范围**是:0x00000000 ~ 0xFFFFFFFF


* 【0,3G】:用户空间
* 【3G,4G】:内核空间


上面的验证代码,在windows下会跑出不一样的结果❗



> 
> 上面的结论,默认**只在Linux下有效!**
> 
> 
> 


### 二. 进程地址空间


#### 🌈 地址空间是什么 (what?)


所以之前所说的“程序地址空间”是不准确的,准确的说是“进程地址空间”,那么什么是**进程地址空间**呢?



> 
> 假设有一个富豪,他有 10 亿美元的家产,而这个富豪他有 3 个私生子,但这 3个私生子此之间并不知道对方的存在,这个富豪对他的每个私生子都说过同一句话:儿子,这10亿的家产未来都是你的。  
>  站在每个私生子的视角中,每个私生子都认为自己拥有 10 亿美元。  
>  如果每个私生子都找父亲一次性要 10 个亿,这个富豪是拿不出来的,但实际上这是不可能的,每个私生子找父亲要钱,一般只会几千几万这样一点点去要,这个富豪只要有,就一定会给。如果私生子要的钱太多,富豪不给,私生子也只会认为是父亲不想给我。  
>  换言之,这个富豪给每个私生子在大脑中建立一个「**虚拟**」的概念:都认为自己拥有 10 亿美元。类比到计算机中:
> 
> 
> * 富豪,称之为**操作系统**
> * 私生子,称之为**一个个独立的进程**
> * 富豪给私生子画的 10 亿家产(大饼),称之为**进程的地址空间**
> 
> 
> 


1️⃣ 我们直接访问物理内存,**特别不安全**(`野指针`等)  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/9b808566849f451788b00bb00c67f691.png)



> 
> 💦所以**让用户不能直接用物理地址**
> 
> 
> 


每个进程都要有一个**地址空间**,操作系统为每一个进程画了一个大饼,它们都认为自己在**独占**物理内存  
 系统中存在大量进程,需要**管理**地址空间,那么就需要`先描述`,~~再组织~~(今天不考虑)


📌内核中的地址空间,本质也一定是一种**数据结构**,将来一定要和特定的**进程关联**起来



struct mm_struct
{
//进程地址空间
}


![在这里插入图片描述](https://img-blog.csdnimg.cn/8ac17bf6ba5f445aa995fd4c6b1cb362.png)


那么我们是如何用struct结构体进行**划分区域**?各个区域又是如何与物理内存**建立关联**的?


#### 🌈 地址空间是如何设计(how?)


##### 🌊区域划分 & 页表映射


💦地址空间是一种内核数据结构,它里面至少要有:**各个区域的划分**  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/3e7a0bd43f8e4f42b28de143520a2d2a.png)


其中堆栈增长会有范围的变化:`本质`其实就是**对start 或者end标记值 ±特定的范围**即可!!



> 
> 地址空间和页表(用户级)是每个进程都**私有一份**
> 
> 
> 


💦那么如何将虚拟地址和物理地址建立**映射关系**呢?通过**页表**


![在这里插入图片描述](https://img-blog.csdnimg.cn/c6572869446f4debaea53b4eb7665f88.png)


只要保证,每一个进程的页表,映射的是物理内存的**不同区域**,就能做到,进程之间不会互相干扰,保证了进程的**独立性**!!


**页表+MMU**进行映射


* `MMU`是Memory Manage Unit的缩写,即**存储管理单元**,是中央处理器用来管理虚拟内存和物理内存寄存器的控制线路,也负责虚拟内存映射为物理内存。


#### 🌈 为什么要有地址空间(why?)


##### 1️⃣保护物理内存


添加一层软件层,完成有效的对进程操作内存的风险管理(**权限管理**),**有效的保护了物理内存**,本质是为了保护物理内存各个进程的`数据安全`  
 凡是**非法的访问**或者映射,`OS`都会**识别到**,**并且终止**你这个进程!!



> 
> OS是怎么样识别到?(后面多线程讲)、OS是怎么样终止的呢?(后面信号讲)卖个关子
> 
> 
> 


类似于过年的压岁钱妈妈帮你收着,等你要用的时候,再来问我要,防止你乱花钱。对应到这里,意味着凡是想使用地址空间和页表进行映射,一定要在`OS的监管`之下访问呢,如果没有中间层(OS),能直接访问物理地址,可能发生非法越界访问。


![在这里插入图片描述](https://img-blog.csdnimg.cn/8820fb0f8a464f27a1c239cedabd4908.png)


在语言层面上,我们知道,字符串存在字符常量区不能修改 ——



char* str = "hero never die;
*str = ‘b’; //不能修改


📌本质上是因为,这里str指针就是虚拟地址,`*`解引用进行写入时,访问虚拟地址,要进行虚拟地址和物理地址的转化,然而OS只给你读`r权限`,就访问不了物理内存了,也就修改不了


![在这里插入图片描述](https://img-blog.csdnimg.cn/406591fada6249468152b07327dc0ba6.png)


##### 2️⃣解耦合 与 延迟分配


🔥`内存管理`模块 vs `进程管理`模块 之间**关联性减少**  
 通过虚拟地址空间,来屏蔽底层申请内存的过程,达到进程读写内存操作和OS进行内存管理进行软件层面上的分离。


如果我们申请了物理空间,但是如果我不立马使用,是不是空间的浪费呢?  
 答:是的 ❗ 本质上,(因为有地址空间的存在,所以上层申请空间,其实是在地址空间上申请的,物理内存可以甚至一个字节都不给你,而当你真正进行对物理地址空间访问的时候,才执行内存的相关管理算法,帮你申请内存,构建页表映射关系),然后才让你进行内存访问的


* 以上**延迟分配**,可以**提高整机的效率**,几乎内存的有效使用率是`100%`



> 
> 上面括号部分是由`OS`**自动完成**,用户包括进程都是**完全0感知**!!  
>  这叫做`基于缺页`中断进行物理内存申请。
> 
> 
> 


ps:好比你妈妈说明天要给你买玩具,但是今晚却把钱借给了二舅,二舅还的上还好,一旦还不上,也就不够钱了对应内存不足的情况


##### 3️⃣进程独立性


➰由于页表的存在,它可以将地址空间上的虚拟地址和物理地址进行映射,那么是不是在进程视角所以的内存分布都可以是有序的!


* 所以`地址空间➕页表`的存在 ,可以将内存分布**有序化**!


➰进程要访问的物理内存中的数据和代码,可能目前并没有在物理内存中,同样的,也可以让不同的进程映射到不同的物理内存,也就实现了进程独立性的实现!!


* **进程的独立性**,可以通过`地址空间➕页表`的方式实现!



> 
> 总结:因为有地址空间的存在,每一个进程都认为自己拥有4GB空间(32),并且各个区域是**有序的**,进而可以通过页表**映射到不同的区域**,来实现进程的**独立性**  
>  ps:每个进程不知道且不需要知道其他进程的存在(好比大富翁的每个私生子)
> 
> 
> 


操作系统(大富翁👨)给每个进程(私生子👶)都给的是全部的地址空间(10G),也就是私生子认为自己是独占的,体现出了**独立性**


### 三. 再次理解


#### ✨写时拷贝


1️⃣ 我们回顾开头的小实验,为什么相同的地址会打印出不同的值?


我们知道子进程是**以父进程为模板**创建出来的  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/c10941910cec42e1ac2bb76cd5e34faf.png)


以至于**父子进程**的`g_val`**虚拟地址和物理地址都一样**


为了维护进程的独立性,子进程在更改时发生**写时拷贝**,即为子进程重新开辟一段物理空间,把有必要的值拷贝过来,再**重新建立**虚拟地址到物理地址的**映射关系** ✅


![在这里插入图片描述](https://img-blog.csdnimg.cn/6ed1a3de76c2431cb5cb6c19355cf3c9.png)


这时我们的**虚拟地址是不变**的,物理内容通过映射被映射到物理内存不同的区域,所以我们看见的值是不一样的  
 🌌根本原因:


* **虚拟地址一样**,所以地址一样;**物理地址不一样**,所以内容不一样


之前说的,父子进程的代码一般是共享的,也就是通过映射到同一段物理空间实现的。


#### ✨重新理解什么是挂起?


经过上面的学习我们知道进程:是**内核数据结构**➕**代码和数据**


![在这里插入图片描述](https://img-blog.csdnimg.cn/b72d29ff306f4e76ad6e653e2777e9c0.png)


加载本质就是创建进程,那么是不是必须非得立马把所有的程序的代码和数据加载到内存中,并且创建内核数据结构建立映射关系?



### 最后的话

最近很多小伙伴找我要Linux学习资料,于是我翻箱倒柜,整理了一些优质资源,涵盖视频、电子书、PPT等共享给大家!

### 资料预览

给大家整理的视频资料:

![](https://img-blog.csdnimg.cn/img_convert/16d0cf4dbf397a3fbdc803262dbe33df.png)

给大家整理的电子书资料:

  

![](https://img-blog.csdnimg.cn/img_convert/73bbad87a64e387e1a06acbaefe34cb4.png)



**如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!**

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/topics/618542503)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
整理的电子书资料:

  

[外链图片转存中...(img-GIIGVfqu-1714169775315)]



**如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!**

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/topics/618542503)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值