运行含MPI库的C++代码时出现Segmentation fault的解决方法
解决MPI并行矩阵乘法的段错误(Signal 11)与进程被杀死(Signal 9)问题
问题概述
在Linux系统中,使用mpic++ -o lab lab.cpp编译一段基于MPI的并行矩阵乘法C++代码后,运行时出现Segmentation fault(信号11)和进程被杀死(信号9)错误,终端错误输出如下:
lab5:8061 terminated with signal 11 at PC=561d993e5815 SP=7ffef0233730. Backtrace: ./lab5(+0x1815)[0x561d993e5815] /lib/x86_64-linux-gnu/libc.so.6(+0x29d90)[0x7f16bb029d90] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80)[0x7f16bb029e40] ./lab5(+0x1365)[0x561d993e5365] lab5:8062 terminated with signal 11 at PC=560003f0e815 SP=7ffd29e58150. Backtrace: ./lab5(+0x1815)[0x560003f0e815] /lib/x86_64-linux-gnu/libc.so.6(+0x29d90)[0x7f9594829d90] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80)[0x7f9594829e40] ./lab5(+0x1365)[0x560003f0e365] =================================================================================== BAD TERMINATION OF ONE OF YOUR APPLICATION PROCESSES PID 8060 RUNNING AT Ubuntu1 EXIT CODE: 9 CLEANING UP REMAINING PROCESSES YOU CAN IGNORE THE BELOW CLEANUP MESSAGES =================================================================================== YOUR APPLICATION TERMINATED WITH THE EXIT STRING: Killed (signal 9) This typically refers to a problem with your application. Please see the FAQ page for debugging suggestions
错误根源分析
未初始化指针访问
- 声明了
double *A, *B但从未分配内存,却在代码末尾调用free(A); free(B);,直接触发段错误。 - 矩阵乘法循环中直接访问
B[k * n + j],但B未分配内存,这是核心段错误触发点。 - 初始化了
subB但未使用,属于逻辑冗余。
- 声明了
超立方体拓扑不匹配
- 代码中
#define N 3对应8节点(2^3)的超立方体拓扑,但如果运行时进程数不为8,MPI_Cart_create会失败,后续使用错误的通信子comm导致异常。
- 代码中
内存分配校验缺失
- 所有
malloc/calloc调用未检查返回值,若内存分配失败会得到空指针,访问空指针触发段错误。
- 所有
Signal 9(OOM Killer)触发原因
- 若进程数与拓扑不匹配,
sub_n = n/size计算出异常值,导致分配超大内存,触发系统OOM Killer杀死进程。
- 若进程数与拓扑不匹配,
修复步骤
修正矩阵B的初始化与分发
- 仅根进程(rank=0)分配完整的
B矩阵内存并初始化,通过MPI_Bcast广播给所有进程。 - 移除未使用的
A指针和subB变量,消除冗余逻辑。
- 仅根进程(rank=0)分配完整的
确保拓扑与进程数匹配
- 运行时必须指定8个进程:
mpiexec -n 8 ./lab。 - 代码中添加进程数检查,若进程数不等于2^N则报错退出。
- 运行时必须指定8个进程:
添加内存分配校验
- 每个
malloc/calloc调用后检查返回值,分配失败则打印错误并调用MPI_Abort退出。
- 每个
修复资源泄漏
- 释放自定义MPI数据类型和通信子,避免资源泄漏。
修正矩阵乘法逻辑
- 确保乘法循环中遍历完整的矩阵维度,保证计算逻辑正确。
完整修复代码
// mpic++ -o lab lab.cpp #include <stdio.h> #include <stdlib.h> #include <math.h> #include <mpi.h> #define N 3 // 超立方体拓扑维度(对应2^3=8个进程) int main(int argc, char **argv) { int size, rank; double *B = NULL, *subA, *subC; int n = 500; // 矩阵大小 int sub_n; // 子矩阵大小 int i, j, k; double start_time, end_time; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &size); MPI_Comm_rank(MPI_COMM_WORLD, &rank); // 检查进程数是否匹配超立方体拓扑要求 if (size != (1 << N)) { if (rank == 0) { fprintf(stderr, "错误:超立方体拓扑需要%d个进程,但当前使用%d个\n", (1<<N), size); } MPI_Abort(MPI_COMM_WORLD, 1); } // 每个进程的子矩阵大小 sub_n = n / size; // 处理n不能被size整除的情况 if (n % size != 0) { if (rank == 0) { fprintf(stderr, "错误:矩阵大小%d不能被进程数%d整除\n", n, size); } MPI_Abort(MPI_COMM_WORLD, 1); } // 分配子矩阵内存并检查 subA = (double *)malloc(sub_n * n * sizeof(double)); if (subA == NULL) { fprintf(stderr, "进程%d:subA内存分配失败\n", rank); MPI_Abort(MPI_COMM_WORLD, 1); } subC = (double *)calloc(sub_n * n, sizeof(double)); if (subC == NULL) { fprintf(stderr, "进程%d:subC内存分配失败\n", rank); MPI_Abort(MPI_COMM_WORLD, 1); } // 初始化子矩阵subA for (i = 0; i < sub_n; i++) { for (j = 0; j < n; j++) { subA[i * n + j] = (1.85 * sqrt((rank * sub_n + i) + sin(j))) / (cos((rank * sub_n + i) + j) + 5); } } // 根进程初始化完整矩阵B并广播 if (rank == 0) { B = (double *)malloc(n * n * sizeof(double)); if (B == NULL) { fprintf(stderr, "进程0:B内存分配失败\n"); MPI_Abort(MPI_COMM_WORLD, 1); } for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { B[i * n + j] = (double)rand() / (double)(RAND_MAX / 40.0) / 4.0; } } } MPI_Bcast(B, n*n, MPI_DOUBLE, 0, MPI_COMM_WORLD); // 初始化超立方体拓扑 int dims[N]; int periods[N]; for (i = 0; i < N; i++) { dims[i] = 2; // 每个维度的节点数 periods[i] = 1; // 周期性 } MPI_Comm comm; int cart_create_err = MPI_Cart_create(MPI_COMM_WORLD, N, dims, periods, 1, &comm); if (cart_create_err != MPI_SUCCESS) { fprintf(stderr, "进程%d:MPI_Cart_create失败,错误码%d\n", rank, cart_create_err); MPI_Abort(MPI_COMM_WORLD, 1); } // 获取当前进程的拓扑坐标 int coords[N]; MPI_Cart_coords(comm, rank, N, coords); // 获取邻居进程(保留拓扑逻辑) int source[N], dest[N]; for (i = 0; i < N; i++) { MPI_Cart_shift(comm, i, 1, &source[i], &dest[i]); } // 并行矩阵乘法计算 MPI_Barrier(comm); start_time = MPI_Wtime(); // 每个进程计算自己的子矩阵subC for (i = 0; i < sub_n; i++) { for (j = 0; j < n; j++) { for (k = 0; k < n; k++) { subC[i * n + j] += subA[i * n + k] * B[k * n + j]; } } } MPI_Barrier(comm); end_time = MPI_Wtime(); // 收集结果到根进程 double *C = NULL; if (rank == 0) { C = (double *)malloc(n * n * sizeof(double)); if (C == NULL) { fprintf(stderr, "进程0:C内存分配失败\n"); MPI_Abort(MPI_COMM_WORLD, 1); } } MPI_Gather(subC, sub_n * n, MPI_DOUBLE, C, sub_n * n, MPI_DOUBLE, 0, comm); // 根进程输出结果 if (rank == 0) { printf("乘法耗时:%f 秒\n", end_time - start_time); // 可选:打印矩阵C(500x500矩阵过大,建议注释或仅打印前几行) /* printf("矩阵C:\n"); for (i = 0; i < 5; i++) { for (j = 0; j < 5; j++) { printf("%f ", C[i * n + j]); } printf("\n"); } */ } // 释放内存 free(subA); free(subC); if (rank == 0) { free(B); free(C); } MPI_Comm_free(&comm); MPI_Finalize(); return 0; }
运行说明
- 编译:
mpic++ -o lab lab.cpp - 运行:
mpiexec -n 8 ./lab(必须指定8个进程,匹配超立方体拓扑要求)
内容的提问来源于stack exchange,提问作者Vlad H
相关产品推荐
相关产品推荐

