释放未被更改的"写时复制"内存

Krz*_*iek 7 c memory-leaks fork copy-on-write

我得到了写拷贝背后的想法.当我fork时,堆被标记为CoW,当任何进程尝试更改它时,会生成一个副本.问题是:我是否必须在孩子的过程中释放它?假设一个父有一个动态char*数组,然后它就是forks.子进程打印一些const char,然后退出.子进程根本没有改变堆.会有内存泄漏吗?

编辑:我的子进程在堆上打印数组,但不修改它.Valgrind说如果我不释放那个阵列就会有泄漏.我释放它时没有泄漏/内存错误.

use*_*367 6

CoW只是一种懒惰的优化.你可以自由地认为,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 …
Run Code Online (Sandbox Code Playgroud)

CoW在fork-exec技术中也是非常有用的优化,其中child只做exec准备参数.exec用指定的可执行映像替换当前进程,保留开放描述符和其他内容(更多内容man 2 execve).在这样的fork之后复制的唯一页面只是当前的堆栈帧,使得fork-exec非常有效.

有些系统还提供vfork了非常严格的不公平版本的fork,但是在没有CoW的系统上,这是有效执行vfork-exec的唯一方法.


cma*_*ter 5

First the logical (process centered) view:

When you fork a process, the entire address space is copied into a new process as is. Your heap is essentially duplicated in both processes, and both processes can continue to use it just like one process could if fork() had never been called. Both processes can free an allocation that was done before the fork(), and they must do so if they want to reuse the address range connected to the allocation. CoW mappings are only an optimization that does not change these semantics.


Now the physical (system centered) view:

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

So, what happens when you call free() in the child before exiting?
Well, the free() function will most likely write to the page containing the memory allocation to tell malloc() that the block is available again. This will trap into the system and copy the page, expect this operation to take a microsecond or two. If your parent process calls free() while the child is still alive, the same will happen. However, if your child does not free the page and exits, the kernel will know that it does not have to perform CoW anymore. If the parent frees and reuses the memory region afterwards, no copy needs to be done.


I assume, that what your child does is simply to check for some error condition, and exit immediately if it is met. In that case, the most prudent approach is to forget about calling free() in the child, and let the system do its work.