子进程中调用free()是否会触发写时复制(copy-on-write)?
关于fork()写时复制与子进程free()的疑问
来自man fork(2)文档:
在Linux系统中,fork()通过写时复制(copy-on-write)页实现,因此它仅需花费复制父进程页表、为子进程创建唯一任务结构的时间与内存开销。
基于此,来看如下示例代码:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> int main(void) { char *data = malloc(100); snprintf(data, 100, "%s", "Hello world!"); pid_t pid = fork(); if (pid == -1) { perror("fork"); exit(EXIT_FAILURE); } if (pid == 0) { // start of child process printf("I'm the child\n"); // Do the child stuff ... puts(data); } else { // start of parent process printf("I'm the parent\n"); // Do the parent stuff ... puts(data); // Wait for child process if (wait(NULL) == -1) { perror("wait"); exit(EXIT_FAILURE); } free(data); } return 0; }
根据我的理解,若资源被复制但未修改,则无需创建新资源,通过malloc分配的内存不会在子进程中复制,子进程仅持有指向父进程内存的指针。
另一方面,若不在子进程中释放资源会导致内存泄漏:
==13024== HEAP SUMMARY: ==13024== in use at exit: 100 bytes in 1 blocks ==13024== total heap usage: 2 allocs, 1 frees, 1,124 bytes allocated ==13024== ==13024== LEAK SUMMARY: ==13024== definitely lost: 100 bytes in 1 blocks ==13024== indirectly lost: 0 bytes in 0 blocks ==13024== possibly lost: 0 bytes in 0 blocks ==13024== still reachable: 0 bytes in 0 blocks ==13024== suppressed: 0 bytes in 0 blocks ==13024== Rerun with --leak-check=full to see details of leaked memory ==13024== ==13024== For lists of detected and suppressed errors, rerun with: -s ==13024== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) ==13023== ==13023== HEAP SUMMARY: ==13023== in use at exit: 0 bytes in 0 blocks ==13023== total heap usage: 2 allocs, 2 frees, 1,124 bytes allocated ==13023== ==13023== All heap blocks were freed -- no leaks are possible ==13023== ==13023== For lists of detected and suppressed errors, rerun with: -s ==13023== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
这表明应该在子进程中调用free():
if (pid == 0) { // start of child process printf("I'm the child\n"); // Do the child stuff ... puts(data); free(data); }
问题
子进程中调用free()是否会触发写时复制(copy-on-write)?
回答
会触发写时复制,但并非针对data指向的堆数据页,而是针对malloc维护的堆元数据页(比如空闲链表、内存块大小记录等)。
当子进程调用free(data)时,需要修改malloc内部的堆管理结构——这些结构存储在特定内存页中,fork后这类页是父子进程共享的只读状态。一旦子进程尝试修改这些元数据,内核就会触发COW,为子进程复制出独立的堆元数据页,后续子进程的堆操作将基于这份副本进行,不会影响父进程的堆管理。
至于data指向的实际数据页,free()本身不会修改数据内容,只是标记该内存块为空闲,因此这份数据页不会被复制。只有当父进程后续访问或修改该数据页时,才会触发COW(如果此前未触发过的话)。
另外需要明确:父子进程的堆是完全独立的,子进程调用free()仅会释放自身地址空间中的内存块标记,父进程的free()操作不受影响,两者互不干扰。
内容的提问来源于stack exchange,提问作者David Ranieri
相关产品推荐
相关产品推荐

