释放未更改的“写时复制”内存

8
我理解写时复制的概念。当我进行fork时,堆被标记为CoW(写时复制),当任何进程尝试更改它时,都会创建一个副本。问题是:在子进程中是否仍然需要释放它?假设父进程有一个动态char *数组,然后进行fork。子进程打印一些const char并退出。子进程根本没有修改堆。这样会造成内存泄漏吗?
编辑:我的子进程在堆上打印数组,但不修改它。如果我不释放该数组,则Valgrind表示存在内存泄漏。释放后没有泄漏/内存错误。
2个回答

7

CoW只是一种懒惰的优化。你可能认为fork()总是在没有延迟的情况下完全复制进程(至少是内存方面)。但是……

如果您准备了动态数据块以“传递”给fork的子进程,那么在fork之后,您就有了两个具有两个动态数据块的进程:父进程和子进程(都是副本)。当子进程退出时,它的内存副本会被回收,但是父进程应该在fork之后自行释放该块。

为了更加清晰,这里有一个例子:

char *buf = malloc(123456);
// … fill buf for child …

int res = fork();

if (res == -1) {
    fprintf(stderr, "fork failed\n");
    exit(EXIT_FAILURE);
}

if (res == 0) {
    // this is child process
    // … do work with buf …
    _Exit(EXIT_SUCCESS); // child reclaims buf by means of exit
}

// this is parent process
free(buf); // we don't need it in parent

// … other parent tasks here …

CoW 在 fork-exec 技术中也非常有用,其中子进程仅使用准备好的参数执行 exec。exec 用指定的可执行映像替换当前进程,保留打开的文件描述符和其他内容(更多信息请参见 man 2 execve)。在这种 fork 后,唯一复制的页面只是当前的栈帧,使 fork-exec 非常有效。
某些系统还提供了 vfork,它是 fork 的非常限制和不公平版本,但在没有 CoW 的系统上,这是实现高效 vfork-exec 的唯一方式。

所以即使实现了CoW并且内存没有被子进程修改,我仍然需要释放它吗? - Krzysiek
@Krzysiek: 是的,你必须释放它。一旦原始程序退出,forked程序中会剩下一份内存副本,需要释放它。如果你没有释放它,forked程序将会不断消耗更多的内存。当然,所有内存都将在程序退出时被释放。 - Zan Lynx
@Krzysiek CoW是一种理论观察的结果,即子进程只更改某些页面,而不是所有页面。 因此,延迟复制可以带来巨大的好处,因为有些页面根本不需要被复制。 这完全是内核端的不可见优化,与常规内存管理无关。请记住:如果您分配了内存,则有责任使用“free”或“exit”释放它。 如果进行fork操作,则责任也加倍。 - user3125367
@Krzysiek 添加了一个示例。 - user3125367

5

首先是逻辑视角(以进程为中心):

当您分叉一个进程时,整个地址空间将按原样复制到一个新的进程中。您的堆在两个进程中都被重复使用,就像如果fork()没有被调用一样。两个进程都可以释放在fork()之前完成的分配,并且如果他们想要重新使用与该分配相关联的地址范围,则必须这样做。 CoW映射只是不改变这些语义的优化。


现在是物理视角(以系统为中心):

您的系统内核不知道您使用malloc()分配的数据范围,它只知道它已经为进程分配了内存页面。当您调用fork()时,它将所有这些页面标记为CoW,并从两个进程引用它们。如果其中任何一个进程在其他进程仍然存在时写入任何CoW页面,则会陷入系统中,而系统会复制整个页面。如果其中一个进程退出,它至少会降低这些页面的引用计数,以使它们不必再被复制。

那么,在子进程退出之前调用free()会发生什么?
好吧,free()函数很可能会写入包含内存分配的页面,以告知malloc()该块再次可用。这将陷入系统并复制页面,预计此操作需要花费一两微秒的时间。如果您的父进程在子进程仍然存在时调用free(),同样会发生这种情况。但是,如果您的子级不释放页面并退出,则内核将知道它不必再执行CoW了。如果父级在此之后释放并重用内存区域,则无需进行复制。


我假设,你的子进程只是检查了一些错误条件,如果满足就立即退出。在这种情况下,最谨慎的方法是忘记在子进程中调用free(),让系统自己完成它的工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接