进程地址空间

在研究程序中不同类型变量的内存分布时,我们通常会使用下面的图片:

进程地址空间_第1张图片

在Linux 系统下运行下面的代码

#include 
#include 
#include 
int g_val = 0;
int main()
{
	pid_t id = fork();
	if (id < 0) 
	{
		perror("fork");
		return 0;
	}
	else if (id == 0) 
	{ //child,子进程肯定先跑完,也就是子进程先修改,完成之后,父进程再读取
		g_val = 100;
		printf("child[%d]: %d : %p\n", getpid(), g_val, &g_val);
	}
	else 
	{ //parent
		sleep(3);
		printf("parent[%d]: %d : %p\n", getpid(), g_val, &g_val);
	}
	sleep(1);
	return 0;
}

运行结果:进程地址空间_第2张图片

虽然同一变量,地址也相同,但里面存储的数据却不相同,从这个结果我们可以得到以下结论:

  • 变量内容不一样, 所以父子进程输出的变量绝对不是同一个变量
  • 但地址值是一样的,说明,该地址绝对不是物理地址!
  • 在Linux地址下,这种地址叫做 虚拟地址
  • 我们在用C / C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理 

那么,操作系统(OS)所起的作用就是将虚拟地址转化为物理地址!

所以之前说‘程序的地址空间’是不准确的,准确的应该说成 进程地址空间,那它到底是什么样的呢?

进程地址空间_第3张图片

每一个进程运行之后,都会有一个自己的进程地址空间的存在,即都要在系统层面有自己的页表映射结构。这些进程地址空间与内存的关系不影响上层语言!子进程会自动继承父进程的页表,直接浅拷贝继承父进程的页表,当子进程或父进程要改变数据时,操作系统会在物理内存中进行写时拷贝,修改改变数据的进程的页表中虚拟地址对应的物理地址,虚拟地址不受改变!

地址空间也要被OS管理起来,每一个进程都要有地址空间和对应的页表,这些地址空间都是虚拟地址空间,系统中,一定要对地址空间做管理!那么如何管理呢?运用操作系统中经常使用的一个概念:先描述,再组织。地址空间最终是一个内核数据结构对象,里面有各种 begin,end 等来对这些地址空间来做管理。

因为进程都是被时间片轮转调度的,所以进程的页表通常都存在进程的上下文中,当调度此进程的时候,会将页表等地址空间信息存储在 CPU 的 CR3 寄存器中来进行使用。但这些页表,是存储在物理内存中的!

进程地址空间_第4张图片

页表中还会有一些其他的字段信息,操作系统通过控制这些信息,可以更好的对数据从虚拟地址到物理地址的管理。如权限访问字段、内存是否被分配字段等 

那为什么要有地址空间呢?

1、一个进程在运行过程中,进程进行各种转换,各种访问,这个进程一定在运行,那么让进程以统一的视角看内存,所以任意一个进程,可以通过地址空间 + 页表的方式,将乱序的内存数据变成有序,分门别类的规划好。无序变有序!

2、存在虚拟地址空间,可以有效的进行进程访问内存的安全检查!

3、将进程管理和内存管理进行解耦!(通过页表,让进程映射到不同的物理内存中,从而实现进程的独立性)

当用户对某一批数据进行写入时,页表也许会因为权限问题而触发重新申请内存,而重新申请的内存拷贝方式就是写时拷贝!

你可能感兴趣的:(Linux,linux,服务器)