Linux进程地址空间

前言引入

内存一般分为代码段、字符常量区…等,想必大家并不陌生,先写一个demo确定各个区域的大致位置
在这里插入图片描述以32位下4G内存为例,打印结果和图片上的区域划分完全符合,栈区向低地址生长,堆区向高地址生长,堆栈相对而生。但是有这样一个疑问,这里打印的地址是物理地址还是虚拟地址?如果不太确定我们可以再写一个demo来验证。
ps:这里我的环境是64位的,但以32位为例
在这里插入图片描述fork创建子进程后,让子进程修改val值进行写时拷贝,那么子进程和父进程的数据val的地址应该是不同的

 #include <iostream>
 #include <unistd.h>

 using namespace std;
 int main()
 {
	 int val = 10;
	 pid_t id = fork();
	 if (id == -1) {//创建失败
		 perror("fork");
		 exit(1);
	 }
	 if (id == 0) {
		 //子进程
		 int count1 = 5;
		 while (count1)
		 {
			 if (count1 == 3)
				 val = 100;//执行到第三次时进行写时拷贝
			 printf("child process addr:%p,val=%d\n", &val, val);
			 sleep(1);
			 --count1;
		 }
	 }
	 else {
		 //父进程
		 int count2 = 5;
		 while (count2)
		 {
			 printf("parent process addr:%p,val=%d\n", &val, val);
			 count2--;
			 sleep(1);
		 }
	 }

	 return 0;
 }

但通过打印却发现写时拷贝后,父子进程的val值不同,但地址仍然相同,说明该地址绝对不是物理地址,而是虚拟地址。
在这里插入图片描述我们在用C/C++语言所看到的地址,或者说用户层所看到的地址,全部都是虚拟地址!物理地址用户一概看不到,由OS统一管理。

进程地址空间

所以上面看到的区域划分图实际上是进程地址空间,也就是是虚拟的空间,实际上就是用一个数据结构描述真实的物理内存,在Linux中定义为struct mm_struct类型。可以认为进程地址空间是操作系统给每个进程画的一张大饼,每个进程都有一个mm_struct类型的数据结构,都认为mm_struct代表整个内存,地址从0x000…0到0xFFF…F,232个地址,这些地址都是虚拟地址也叫做线性地址,让每个进程都认为自己独占4G内存资源,地址空间是按照4G划分的,这样就可以让进程以统一的视角看待物理内存。所以进程在创建时,除了创建进程控制块task_struct,还会创建一个数据结mm_struct。
下图为mm_struct的部分声明
在这里插入图片描述根据冯诺依曼机基本原理,任何需要执行的程序和处理的数据都必须先加载到物理内存才能运行,那么是如何将虚拟地址装换成物理地址的呢?

实际上在创建进程时,除了创建PCB,mm_struct,还会创建一个数据结构----页表,可以理解为一张hash映射表,将虚拟地址映射到对应的物理地址,这个工作需要用到一个硬件设备MMU(memory manage unit)内存管理单元,它负责虚拟地址到物理地址的映射,并提供硬件机制的内存访问权限的检查。
在这里插入图片描述

进程地址空间的好处

好处一:
就好比我们小时候会将压岁钱给妈妈保管一样,妈妈怕我们把钱搞丢了,怕我们被骗了,怕我们一下子把钱都用完了,等我们要用钱的时候再找她要。OS也同样如此,通过添加一层软件层,可以有效的对进程操作内存进行风险管理(权限管理),本质目的是为了保护物理内存和各个进程的数据安全。如果直接访问物理内存,A进程越界就可能把B进程的数据改了。就像我们定义一个常量字符串const char* str = “process”;无法被更改,本质上是操作系统只给了r读权限。

好处二:
有这样一个问题,如果我们申请了1M的空间但暂时不全部使用或暂时不使用,操作系统会将空间立马给我们吗?
站在操作系统的角度,如果将空间立马给你,意味着会有部分空间被你闲置着(部分空间长时间没有读取操作),本来可以给其他进程使用的。有了进程地址空间,可以将内存申请和内存使用的概念在时间上划分清楚,通过虚拟地址空间,屏蔽底层申请空间的过程,让内存申请动作变得透明,达到进程读写内存和系统进行内存管理操作,进行软件上的分离。你申请空间实际上是在进程地址空间上申请的,这些都是虚拟空间,当你真正要使用时(对内存进行读写操作),系统再进行缺页中断,把空间给你申请好,充分的利用资源。

好处三:
让每个进程都认为自己独占内存资源相互不受影响不需要考虑是否占用其他进程的资源,大大降低了内存管理和进程管理的耦合性,让进程具有独立性,大大降低了对进程的管理成本。

讲到这里,我们就可以解释最开始进行写时拷贝而打印的地址却是一样的。在进行写时拷贝之前,父子进程的数据时是共享的,val的虚拟地址和物理地址都相同,但子进程修改val发生写时拷贝时,OS就会给它在物理内存上重新开辟空间,再把原来的数据拷贝过来然后让子进程修改,虽然它们的虚拟地址仍然相同,但子进程的虚拟地址映射到的物理地址和父进程的虚拟地址映射到的物理地址是不同的。
在这里插入图片描述

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寄一片海给你

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值