当前位置: 首页 > >

进程地址空间

发布时间:

在前面我们已经介绍了进程相关的基本概念,错过的童鞋们可以戳这里:
这篇文章为大家总结一下Linux中进程地址空间相关知识:


进程地址空间

在这之前大家得先明白一个概念:


地址:指向内存区域的一个编号

假设我们有4GB的内存空间,我们不可能为每一个进程都分配满满4个GB的内存空间,所以我们很有必要了解一下内存的基本布局



那么系统到底是如何分配内存的呢?内存分配的方式其实经过了几次演变:


一、早期的内存分派机制


程序运行时之间装入内存,内存空间不够时需要将已运行程序的数据拷贝出来给新的程序,直接在物理内存上操作,不安全,如图:



二、分段管理机制


代码段和数据段地址从0开始,统一由OS映射管理,CPU将内存分段管理,使用虚拟地址,以偏移地址来计算,安全性高。程序运行地址确定,越界易判断,但没有解决性能问题,如图:



三、分页管理+虚拟地址空间


在之前的基础上采用页表进行分页管理,以及写实拷贝技术,大大提高了安全性和性能,如图:



虚拟地址空间mm_struct结构体中的结构及关系:



如上的分页管理+虚拟地址空间我们可以用如下代码来验证:


父进程创建一个子进程,父子进程分别打印自己的pid、value值、value地址


#include
#include
#include

int value = 0;

int main()
{
pid_t pid = fork();
if(pid < 0){
perror("fork");
exit(1);
}
else if(pid == 0){//child
printf("I am child: %d, value = %d, %p
", getpid(), value, &value);
}
else{ //parent
printf("I am parent: %d, value = %d, %p
", getpid(), value, &value);
}
sleep(1);
return 0;
}


结果发现父子进程打印的变量值和地址都相同,因为子进程并未对变量进行任何修改,我们在修改上述代码,在子进程中将value值改为10,再次运行:


#include
#include
#include

int value = 0;

int main()
{
pid_t pid = fork();
if(pid < 0){
perror("fork");
exit(1);
}
else if(pid == 0){//child
value = 10;
printf("I am child: %d, value = %d, %p
", getpid(), value, &value);
}
else{ //parent
printf("I am parent: %d, value = %d, %p
", getpid(), value, &value);
}
sleep(1);
return 0;
}


子进程中的value值已经被修改了,但是父子进程中value的地址却相同,所以:父子进程中的value不是同一个变量,但是地址一样,所以这个地址肯定不是物理地址,而是我们上面讲到的虚拟地址。



友情链接: