程序地址空间
我们在学习C语言的时候,曾经在对函数调用学习的时候,曾经提到过一个所谓的内存空间,也就是程序地址空间。我们在对程序地址空间学习的时候,了解了在程序地址空间内,从下自上分别是代码区、只读常量区、已初始化全局数据区、未初始化全局数据区、堆区、栈区。
我们知道fork()函数可以创建子进程,而fork()之后产生的子进程,与其父进程共享代码,并且数据各自私有一份,当任意一个写入数据时,发生写时拷贝。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int value = 0;
int main()
{
pid_t id = fork();//利用fork产生子进程后的返回值来让父子执行不同的代码
if(id < 0)
{
perror("fork");
return 0;
}
else if(id == 0)
{//子进程
printf("child[%d] : %d : %p\n",getpid(),value,&value);
}
else
{//父进程
printf("father[%d] : %d : %p\n",getpid(),value,&value);
}
sleep(1);
return 0;
}
我们可以看到,子进程与父进程中value的地址是相同的,也就是说他们确实是共用一块代码,我们对这个代码稍加改动。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int value = 0;
int main()
{
pid_t id = fork();//利用fork产生子进程后的返回值来让父子执行不同的代码
if(id < 0)
{
perror("fork");
return 0;
}
else if(id == 0)
{//子进程
value = 100;
printf("child[%d] : %d : %p\n",getpid(),value,&value);
}
else
{//父进程
printf("father[%d] : %d : %p\n",getpid(),value,&value);
}
sleep(1);
return 0;
}
我们发现,这个时候子进程跟父进程两个的value是同一块地址,但是为什么其value的值不一样呢?按照我们的程序地址空间来说,同一个地址内的内容应该是一样的呀,怎么会这样呢?
其实我们的程序地址空间是进程(PCB)task_struct结构体内的一个成员,而这个程序地址空间上面的内容其实都是虚拟的,也就是说它上面所说的内存并不是真正的物理内存。而真正的物理内存需要程序地址空间这个虚拟的内存通过页表来映射。
当我们调用fork()函数的时候,父子进程在物理地址上共用一份代码,并且数据各自私有,但是其对应的虚拟地址空间的内容是一样的。如下图:
所以当我们子进程的value的值时,父进程与子进程看似value地址一样,实则在物理内存上并不一致。所以其互不影响。
其实计算机如此设计目的就是为了更好的保护物理内存,防止物理内存被破坏以及侵占。
欢迎大家共同讨论,如有错误及时联系作者指出,并改正。谢谢大家!