如何用C语言fork()创建多进程读写共享数组实现任务分摊
C语言多进程共享内存处理大数组方案
针对你提出的多进程处理大数组、避免内存重复占用的需求,核心解决方案是使用共享内存机制,让父子进程直接访问同一块物理内存,无需fork时复制整个地址空间。下面提供两种实用实现方式:
1. 匿名内存映射(mmap)
这种方式简洁高效,专为父子进程共享内存设计,无需额外的内存标识符管理。
实现思路
- 用
mmap创建匿名共享内存块,同时存储原N×M数组和结果数组 - 父进程填充原数组的随机整数
- 拆分N行给K个子进程,每个子进程独立统计分配到的行的奇数数量,写入结果数组对应位置
- 父进程等待所有子进程完成后,读取结果
代码示例
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/mman.h> #include <sys/wait.h> #include <time.h> int main() { int N = 100; // 行数(可按需调整) int M = 1000; // 每行元素数(可按需调整) int K = 4; // 进程数(可按需调整) // 计算总内存:原数组(N*M个int) + 结果数组(N个int) size_t total_size = (N * M + N) * sizeof(int); // 创建匿名共享内存,父子进程共享同一块物理内存 void *shared_mem = mmap(NULL, total_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); if (shared_mem == MAP_FAILED) { perror("mmap failed"); exit(EXIT_FAILURE); } // 映射原数组和结果数组的指针 int (*arr)[M] = (int (*)[M])shared_mem; int *result = (int *)((char *)shared_mem + N * M * sizeof(int)); // 父进程填充随机整数 srand(time(NULL)); for (int i = 0; i < N; i++) { for (int j = 0; j < M; j++) { arr[i][j] = rand() % 1000; } } // 创建K个子进程 for (int i = 0; i < K; i++) { pid_t pid = fork(); if (pid == -1) { perror("fork failed"); exit(EXIT_FAILURE); } else if (pid == 0) { // 子进程分配行范围:最后一个进程处理剩余行 int start = i * (N / K); int end = (i == K-1) ? N : (i+1)*(N/K); // 统计每行奇数数量 for (int row = start; row < end; row++) { int count = 0; for (int col = 0; col < M; col++) { if (arr[row][col] % 2 != 0) count++; } result[row] = count; } // 子进程完成任务后退出,自动释放内存映射 munmap(shared_mem, total_size); exit(EXIT_SUCCESS); } } // 父进程等待所有子进程结束 for (int i = 0; i < K; i++) { wait(NULL); } // 验证结果(可选) printf("前5行的奇数数量:\n"); for (int i = 0; i < 5; i++) { printf("行%d:%d\n", i, result[i]); } // 释放共享内存 munmap(shared_mem, total_size); return 0; }
2. System V共享内存(shmget/shmat)
这种方式更灵活,支持非父子进程间的内存共享,适合复杂场景。
实现思路
- 用
shmget创建共享内存段,获取唯一标识符 - 用
shmat将内存段映射到进程地址空间 - 后续逻辑与匿名映射一致,完成后需手动删除共享内存
代码示例
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/wait.h> #include <time.h> int main() { int N = 100; int M = 1000; int K = 4; size_t total_size = (N * M + N) * sizeof(int); // 创建私有共享内存段,仅父子进程可用 int shmid = shmget(IPC_PRIVATE, total_size, IPC_CREAT | 0666); if (shmid == -1) { perror("shmget failed"); exit(EXIT_FAILURE); } // 将共享内存映射到进程地址空间 void *shared_mem = shmat(shmid, NULL, 0); if (shared_mem == (void *)-1) { perror("shmat failed"); exit(EXIT_FAILURE); } int (*arr)[M] = (int (*)[M])shared_mem; int *result = (int *)((char *)shared_mem + N * M * sizeof(int)); // 填充随机整数 srand(time(NULL)); for (int i = 0; i < N; i++) { for (int j = 0; j < M; j++) { arr[i][j] = rand() % 1000; } } // 创建子进程处理任务 for (int i = 0; i < K; i++) { pid_t pid = fork(); if (pid == -1) { perror("fork failed"); exit(EXIT_FAILURE); } else if (pid == 0) { int start = i * (N / K); int end = (i == K-1) ? N : (i+1)*(N/K); for (int row = start; row < end; row++) { int count = 0; for (int col = 0; col < M; col++) { if (arr[row][col] % 2 != 0) count++; } result[row] = count; } // 子进程解除内存映射 shmdt(shared_mem); exit(EXIT_SUCCESS); } } // 等待子进程完成 for (int i = 0; i < K; i++) { wait(NULL); } // 验证结果 printf("前5行的奇数数量:\n"); for (int i = 0; i < 5; i++) { printf("行%d:%d\n", i, result[i]); } // 父进程解除映射并删除共享内存 shmdt(shared_mem); shmctl(shmid, IPC_RMID, NULL); return 0; }
关键说明
- 无需同步:你的场景中每个子进程处理独立的行,不会出现内存竞争,因此不需要信号量或互斥锁
- 编译方式:直接用gcc编译即可,无需额外链接库:
gcc -o shared_demo shared_demo.c - 内存回收:匿名mmap会在所有进程退出后自动回收;System V共享内存需手动调用
shmctl删除
内容的提问来源于stack exchange,提问作者Kevcsd1
相关产品推荐
相关产品推荐

