【Linux】进程第三弹(虚拟地址空间)

目录

现象

底层原因

数据不发生修改

数据修改

小总结

地址空间本质

为什么要有地址空间


现象

来看代码:

cpp 复制代码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int val = 50;

int main()
{
	printf("father process is running,pid is:%d,ppid is:%d,val:%d,&val:%p\n", getpid(), getppid(), val, &val);
	pid_t id = fork();
	if (id == 0)
	{
		int cnt = 0;
		while (1)
		{
			printf("child process,val:%d,&val:%p\n", val, &val);
			cnt++;
			sleep(1);

			if (cnt == 3)
			{
				val = 100;
				printf("child proess change 50->100\n");
				printf("child process,val:%d,&val:%p\n", val, &val);
				sleep(1);
			}
		}
	}

	else if (id > 0)
	{
		while (1)
		{
			printf("father process,val:%d,&val:%p\n", val, &val);
			sleep(1);
		}
	}


	return 0;
}

如上述代码,定义一个全局变量val,分别在父子进程中打印内容和地址,当在子进程运行3s后,更改其值。来看看结果。

从上图不难看出,没修改前父子进程的val都是50,地址也一样;当在子进程修改后,子进程打印出来了100,而父进程还是50,但是地址还是一样的!!很奇怪!!?

  • 同一变量名输出不同的内容,说明父子进程中用的并不是同一个变量。
  • 每一个变量都会有属于自己的物理空间(地址)。。说明,使用程序代码打印出来的这个地址绝对不是物理地址!
  • 实际上,在Linux中,对于这样的地址就被称为虚拟地址!平时我们采用C/C++打印出来的地址,都是虚拟地址,并不是数据真正在内存中的地址!
  • OS在创建进程时,不仅仅将进程的代码和数据加载到内存,还会为每一个进程创建一个地址空间,这个地址空间就是虚拟地址空间!!

既然是虚拟地址,那又怎么找到当前数据所在内存的位置?只有知道位置,才能进行数据的访问和修改。因为内存和代码被加载到了物理内存中。。

实际上OS也会为每个进程建立一个页表,这个页表能够将虚拟地址和物理地址建立起映射的关系!操作系统会自动将虚拟地址转化为物理地址。。

底层原因

数据不发生修改

  • 这种情况父子进程的页表映射的是同一个地址。因为子进程会继承父进程内容,父进程的代码和数据子进程都能看到(不能修改)

数据修改

  • 这种情况会发生OS发生写时拷贝。OS会在内存重新找一块空间,将数据拷贝下来再做修改,并重新更新子进程的页表,重新建立新的映射关系。

为什么要发生写时拷贝?

  • 因为进程具有独立性,多进程运行,都需要独享各种资源,进程之间不能相互干扰。
    能不能在创建子进程的时候就将父进程的数据全部写时拷贝呢?

  • 可以,但是没必要,因为子进程不一定会全部修改父进程的数据,写时拷贝是有一定的消耗的,应该要按需分配,修改的时候在写时拷贝,这样更能节省消耗!

小总结

①上述程序运行的结果足矣说明,同一个变量,地址相同,本质是虚拟地址相同,内容不同本质是被映射到的物理地址不同!

②这也解释了fork函数,同一个id变量名为什么既能>0,又能小于0的原因。因为fork函数会返回两次,即return,return的本质就是写入(修改),发生写时拷贝!

cpp 复制代码
//fork会return,对父进程的id写入,发生写时拷贝!
pid_t id=fork();
if(id==0)
{
    //child
    ....
}
else if(id>0)
{    
    //father
    .....
}

地址空间本质

进程地址空间,在内存中是一种内核数据结构,在Linux中本质就是一个mm_struct的结构体,这个结构体内部的属性都是表示某一个区域的范围。。

就好像一把直尺,每一部分的内容都有属于自己的范围!

为什么要有地址空间

①拦截非法请求,变相保护物理内存!

  • 如果没有地址空间,进程直接访问物理内存,极大的增加系统异常的概率。
  • 加上地址空间,进程首先得先通过地址空间这一关,比如访问数组越界等异常问题,地址空间会立马报错,阻止进程继续访问物理内存!

②进程管理模块和内存模块的解耦,实际是提高内存利用率。

  • 如果在创建进程的时候,直接申请物理内存。若进程长时间不使用物理内存,那必然会造成资源的浪费
  • 有了虚拟地址空间,可以在页表先申请出虚拟地址,当进程实际需要使用内存,再申请内存空间,并建立映射关系,极大提高内存利用率。

③将无序变有序,让进程以统一的视角看待物理内存以及自己运行的各个区域。

  • 有了地址空间,每个进程都无需关心具体的数据在物理内存中的位置,只需知道虚拟地址,OS会通过页表映射到对应的物理位置!

好了,今天的内容就分享到这里,如果你有所收获,欢迎三连,xd!

相关推荐
o(╥﹏╥)8 分钟前
在 Ubuntu 上安装 VS Code
linux·运维·vscode·ubuntu·vs
AI慧聚堂33 分钟前
自动化 + 人工智能:投标行业的未来是什么样的?
运维·人工智能·自动化
不爱学英文的码字机器35 分钟前
[Linux] Shell 命令及运行原理
linux·运维·服务器
cdut_suye1 小时前
Linux工具使用指南:从apt管理、gcc编译到makefile构建与gdb调试
java·linux·运维·服务器·c++·人工智能·python
qq_433618441 小时前
shell 编程(三)
linux·运维·服务器
苹果醋31 小时前
2020重新出发,MySql基础,MySql表数据操作
java·运维·spring boot·mysql·nginx
两张不够花1 小时前
Jenkins 持续集成部署
运维·jenkins
Tlzns1 小时前
Linux网络——UDP的运用
linux·网络·udp
码农土豆1 小时前
PaddlePaddle飞桨Linux系统Docker版安装
linux·docker·paddlepaddle
Hacker_xingchen1 小时前
天融信Linux系统安全问题
linux·运维·系统安全