【问题标题】:Virtual and physical addresses after fork分叉后的虚拟地址和物理地址
【发布时间】:2020-08-31 18:13:08
【问题描述】:

fork 后子进程的虚拟地址空间在幕后发生了什么?

我一直在阅读其他帖子,但有些人所说的与其他人所说的相反,这有点搞砸了我的想法。

以这段代码为例:

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
    }
}

为什么%p 的值在两个 printf 之后都相同,但是当我在子节点中更改 a 的值时,它不会反映在父节点上。打印%p时,是指变量的虚拟地址?

子节点的虚拟地址是否与父节点相同(在对堆进行任何更改之前), 或者新的子进程有自己的虚拟地址,指向父进程的相同物理地址(只要堆中的变量没有改变)?

我读到,当对位于子堆上的变量进行更改时,物理内存被克隆,虚拟地址现在指向这个新克隆的物理地址(现在变量已更改)——一个进程称为写时复制。

【问题讨论】:

  • 您看到的所有地址都是虚拟的。物理地址对您的程序完全隐藏。除非您正在编写操作系统内核,否则它们并不重要。分叉进程的内存是父进程内存的精确副本。这包括所有事物的地址。
  • 如果你想查看实际的物理地址,要么使用现有的操作系​​统内核,要么使用一些 MS-DOS 实模式程序(因为实模式不支持内存虚拟化(有一些小警告) ),那里使用的地址是实际的物理内存地址。

标签: c


【解决方案1】:

为什么在两个 printf 之后 %p 的值是一样的,但是当我改变孩子中 a 的值时,它并没有反映在父级上。打印%p的时候是指变量的虚拟地址吗?

移动对象的虚拟地址会导致程序崩溃。它们在变量中存储对象的虚拟地址。在fork 之后,两个实例具有相同布局的虚拟地址空间。

子进程的虚拟地址是否与父进程相同(在对堆进行任何更改之前),或者新的子进程有自己的虚拟地址,指向父进程的相同物理地址(只要因为堆中的变量没有改变)?

我不明白这是一个有意义的区别。

我读到,当对位于子堆上的变量进行更改时,物理内存被克隆,虚拟地址现在指向这个新克隆的物理地址(现在变量已更改)——一个进程称为写时复制。

没错。这避免了必须在调用fork 的进程映射的每个页面的物理内存中制作两个副本。

【讨论】:

  • 关于“我不明白这是一个有意义的区别”:在前者中,子对象和父对象都具有某个对象 x 的虚拟地址 100。在后者中,子节点的地址为 4100,父节点的地址为 100,并且都指向同一个物理地址。前者是正确的,对于自 fork 以来未修改的页面,取决于操作系统是否这样做。
  • @EricPostpischil 正如我理解他所说的那样,他明白它们在数字上是相同的。 “它自己的虚拟地址”,我不认为他是指它自己的数值。
  • @EricPostpischil 我想说的是以下内容。假设父进程具有以下虚拟地址:100,200,300,并且地址 100 映射到物理地址 800(存储 a 的位置)。调用 fork 时,虚拟地址是否与父 (100,200,300) 相同?当试图改变a的值时,子进程的虚拟地址100现在是否映射到另一个物理地址即900,它对应于存储变量但现在分配新更改的值的物理地址800的副本?
  • @FRANCISCOBERNAD 您似乎倒退了。例如:pid_t pid; pid_t *ptr = &pid; fork(); *ptr = getpid(); 如果pid 的虚拟地址在调用fork() 之前在父进程中为0x100,则在子进程中也必须为0x100,因为ptr 中的值在两者流程不会在叉子上发生变化。 COW 意味着一个新的物理页面从原始页面复制,然后映射到相同的虚拟地址。没有其他方法可以工作,因为无法确定地址空间中的 0x100 值指的是什么。
  • (cont) 位只是位,进程地址空间中的值0x100 可能是一个地址,也可能是一个计数。虚拟内存管理器不知道进程将来如何解释该值,因此父进程和子进程中页面的虚拟地址必须相同,以确保对这些页面中对象的引用保持有效。跨度>
【解决方案2】:

当调用 fork 时,虚拟地址空间(以及文件描述符)被复制到新进程中。这意味着对于所有意图和目的,这两个过程都是相同的。

现在为了确保两个进程保持独立,所有物理内存都设为只读。当尝试写入时,会引发处理器异常。然后内核在一个新页面中分页并从原始页面复制数据。然后它重新运行该进程并允许写入。

为此,处理器必须具有将物理内存映射到虚拟空间的内存管理单元。这意味着未经修改的 Linux 无法在微控制器单元上运行。

【讨论】:

    猜你喜欢
    • 2015-05-04
    • 2013-05-05
    • 1970-01-01
    • 2014-03-15
    • 2014-09-14
    • 1970-01-01
    • 2017-07-07
    • 2012-02-18
    相关资源
    最近更新 更多