问题描述
在分叉之后,子进程的虚拟地址空间在后台发生了什么?
我一直在阅读其他文章,但是有些人的说法与其他人的说法背道而驰,这有点弄乱了我的主意。
以下面的代码为例:
int main(int argc,char const *argv[])
{
int a = 5;
dummy(&a);
return 0;
}
void dummy(int * a){
if(fork()==0){
printf("%p",a);
*a=10;
}
else{
printf("%p",a);
printf("%d\n",*a); //a is still 5
}
}
为什么在两个printf之后%p
的值都相同,但是当我在子级中更改a
的值时,它不会反映在父级上。打印%p
时,它是指变量的虚拟地址吗?
子级的虚拟地址和父级的虚拟地址是否相同(在对堆进行任何更改之前), 还是新的子进程具有自己的虚拟地址,该地址指向父进程的相同物理地址(只要不对堆中的变量进行任何更改)?
我已经读到,对位于子堆上的变量进行更改时,将克隆物理内存,并且虚拟地址现在指向此新克隆的物理地址(该变量现在已更改)—一个过程被称为写时复制。
解决方法
为什么在两个printf之后%p的值相同,但是当我在子级中更改a的值时,它不会反映在父级上。打印%p时,它是指变量的虚拟地址吗?
在对象的虚拟地址周围移动会导致程序崩溃。他们将虚拟地址存储到变量中的对象。在fork
之后,两个实例都具有具有相同布局的虚拟地址空间。
子进程的虚拟地址是否与父进程的虚拟地址相同(在对堆进行任何更改之前),或者新的子进程具有自己的虚拟地址,它们指向父进程的相同物理地址(只要因为没有对堆中的变量进行任何更改)?
我不明白这是什么有意义的区别。
我已经读到,对位于子堆上的变量进行更改时,将克隆物理内存,并且虚拟地址现在指向此新克隆的物理地址(该变量现在已更改)—一个过程被称为写时复制。
是的。这样避免了调用fork
的进程已映射的每个页面的物理内存必须制作两个副本。
调用fork时,虚拟地址空间(以及文件描述符)被复制到新进程中。这意味着这两个过程在所有意图和目的上都是相同的。
现在,为了确保两个进程保持独立,所有物理内存均设为只读。尝试写入时,将引发处理器异常。内核将页面放入新页面,并从原始页面复制数据。它会重新运行该过程并允许写入。
为此,处理器必须具有将物理内存映射到虚拟空间的内存管理单元。这意味着未修改的Linux无法在微控制器单元上运行。