启用Copy-on-Write仍fork失败?RockyLinux内存分配问题求助
问题:写时复制机制下fork子进程仍失败的原因及解决
我在一台4TB内存的RockyLinux 8.10服务器上运行如下程序:父进程先通过malloc分配45GB内存,再fork出100个子进程。由于fork采用写时复制(Copy-on-Write)机制,我认为所有子进程可以共享这45GB内存,无需实际复制物理内存,但程序却报错:
Failed to fork 0 (total of 0GB allocated): Cannot allocate memory
程序未对mem数组执行任何写入操作,按预期应该能成功运行,请问问题出在哪?是否需要配置系统参数来启用写时复制?
附上代码:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <signal.h> #define GB 45 #define FORKS 100 void main() { unsigned char *mem[GB]; int i; for (i = 0; i < GB; i++) { mem[i] = malloc(1024*1024*1024); if (mem[i] == NULL) { printf("Failed to alloc %d: %s\n", i, strerror(errno)); return; } } int pids[FORKS]; for (i =0; i < FORKS; i++) { int pid = fork(); if (pid == 0) { sleep(20); exit(0); } if (pid == -1) { printf("Failed to fork %d (total of %dGB allocated): %s\n", i, i*GB, strerror(errno)); for (int j = 0; j < pid; j++) { kill(pids[j], SIGTERM); } return; } pids[i] = pid; } for (int j = 0; j < FORKS; j++) { kill(pids[j], SIGTERM); } printf("Success\n"); }
核心原因:虚拟内存额度检查与OOM机制
写时复制是Linux内核默认启用的机制,不需要额外配置,问题出在fork时的虚拟内存预留检查:
- 内核fork子进程时,会假设子进程可能会写入所有共享的内存页,因此会检查系统是否能为子进程预留足够的虚拟内存地址空间(即使实际不会分配物理内存)。
- 你的场景中,父进程已占用45GB虚拟内存,每个子进程创建时,内核会认为它需要额外45GB虚拟内存额度,100个子进程+父进程总共需要
45GB * 101 = 4545GB的虚拟内存空间。如果系统的物理内存+swap总和不足这个值,或者进程的虚拟内存限制(RLIMIT_AS)低于这个值,就会触发Cannot allocate memory错误。
解决方法
1. 调整进程虚拟内存限制
用ulimit -v查看当前进程的虚拟内存限制(RLIMIT_AS),默认可能是无限制,但如果系统虚拟内存(物理+swap)不足,仍会失败。
临时调整限制:
ulimit -v unlimited
或者在代码中通过系统调用永久修改(需在内存分配前执行):
#include <sys/resource.h> // 在malloc循环前添加 struct rlimit rl; getrlimit(RLIMIT_AS, &rl); rl.rlim_cur = RLIM_INFINITY; // 设置为无限制 setrlimit(RLIMIT_AS, &rl);
2. 优化内存分配方式
代码中多次malloc 1GB会产生内存碎片,改为一次性分配45GB内存,能让内核更高效管理虚拟内存:
unsigned char *mem = malloc(45LL * 1024 * 1024 * 1024); if (!mem) { printf("Failed to alloc: %s\n", strerror(errno)); return; }
3. 检查并扩展swap空间
如果物理内存4TB但swap过小,内核计算虚拟内存可用量时会受限。用swapon -s查看swap大小,必要时新增swap分区或文件。
4. 修复代码中的bug
- 错误提示里的
total of %dGB allocated逻辑错误,应该是固定的45GB,而非i*GB; - fork失败后的kill循环条件错误,
j < pid应改为j < i(因为pid=-1,此时已成功创建了i个子进程):
printf("Failed to fork %d (total of %dGB allocated): %s\n", i, GB, strerror(errno)); for (int j = 0; j < i; j++) { kill(pids[j], SIGTERM); }
内容的提问来源于stack exchange,提问作者puneet336
相关产品推荐
相关产品推荐

