gpt4 book ai didi

c - fork() 中的写时复制如何工作?

转载 作者:IT王子 更新时间:2023-10-29 00:25:03 24 4
gpt4 key购买 nike

我想知道 fork() 中的写时复制是如何发生的。

假设我们有一个进程 A,它有一个动态 int 数组:

int *array = malloc(1000000*sizeof(int));

数组中的元素被初始化为一些有意义的值。然后,我们使用 fork() 创建一个子进程,即 B。B 将迭代数组并进行一些计算:

for(a in array){
a = a+1;
}
  1. 我知道B不会马上复制整个数组,但是子B什么时候为数组分配内存呢?在 fork() 期间?
  2. 它是一次分配整个数组,还是只为 a = a+1 分配一个整数?
  3. a = a+1; 这是怎么发生的? B 是否从 A 读取数据并将新数据写入自己的数组?

我写了一些代码来探索 COW 的工作原理。我的环境:ubuntu 14.04,gcc4.8.2

#include <stdlib.h>
#include <stdio.h>
#include <sys/sysinfo.h>

void printMemStat(){
struct sysinfo si;
sysinfo(&si);
printf("===\n");
printf("Total: %llu\n", si.totalram);
printf("Free: %llu\n", si.freeram);
}

int main(){
long len = 200000000;
long *array = malloc(len*sizeof(long));
long i = 0;
for(; i<len; i++){
array[i] = i;
}

printMemStat();
if(fork()==0){
/*child*/
printMemStat();

i = 0;
for(; i<len/2; i++){
array[i] = i+1;
}

printMemStat();

i = 0;
for(; i<len; i++){
array[i] = i+1;
}

printMemStat();

}else{
/*parent*/
int times=10;
while(times-- > 0){
sleep(1);
}
}
return 0;
}

fork()后,子进程修改数组中的一半数,然后修改整个数组。输出是:

===
Total: 16694571008
Free: 2129162240
===
Total: 16694571008
Free: 2126106624
===
Total: 16694571008
Free: 1325101056
===
Total: 16694571008
Free: 533794816

看来数组并没有整体分配。如果我将第一个修改阶段略微更改为:

i = 0;
for(; i<len/2; i++){
array[i*2] = i+1;
}

输出将是:

===
Total: 16694571008
Free: 2129924096
===
Total: 16694571008
Free: 2126868480
===
Total: 16694571008
Free: 526987264
===
Total: 16694571008
Free: 526987264

最佳答案

取决于操作系统、硬件架构和 libc。但是,对于最近带有 MMU 的 Linux,是的 fork(2)将与写时复制一起使用。它只会(分配和)复制一些系统结构和页表,但堆页面实际上指向父页面直到写入。

可以使用 clone(2) 对此进行更多控制称呼。和 vfork(2) beeing 一个特殊的变体,它不希望页面被使用。这通常在 exec() 之前使用。

至于分配:malloc() 具有关于请求的内存块(地址和大小)的元信息,C 变量是一个指针(在进程内存堆和堆栈中)。这两个对于 child 来说看起来是一样的(相同的值是因为在两个进程的地址空间中看到相同的底层内存页面)。所以从 C 程序的角度来看,数组已经分配并且变量在进程开始存在时初始化。然而,底层内存页面指向父进程的原始物理页面,因此在修改之前不需要额外的内存页面。

如果子进程分配一个新数组,则取决于它是否适合已经存在的堆页面,或者是否需要增加进程的 brk。在这两种情况下,只有修改后的页面被复制,新页面只被分配给 child 。

这也意味着物理内存可能会在 malloc() 之后用完。 (这很糟糕,因为程序无法检查“随机代码行中的操作”的错误返回码)。一些操作系统不允许这种形式的过度使用:所以如果你 fork 一个进程,它不会分配页面,但它要求它们在那一刻可用(某种程度上保留它们)以防万一。在 Linux 中,这是 configurable and called overcommit-accounting .

关于c - fork() 中的写时复制如何工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27161412/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com