You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

运行含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

错误根源分析

  1. 未初始化指针访问

    • 声明了double *A, *B但从未分配内存,却在代码末尾调用free(A); free(B);,直接触发段错误。
    • 矩阵乘法循环中直接访问B[k * n + j],但B未分配内存,这是核心段错误触发点。
    • 初始化了subB但未使用,属于逻辑冗余。
  2. 超立方体拓扑不匹配

    • 代码中#define N 3对应8节点(2^3)的超立方体拓扑,但如果运行时进程数不为8,MPI_Cart_create会失败,后续使用错误的通信子comm导致异常。
  3. 内存分配校验缺失

    • 所有malloc/calloc调用未检查返回值,若内存分配失败会得到空指针,访问空指针触发段错误。
  4. Signal 9(OOM Killer)触发原因

    • 若进程数与拓扑不匹配,sub_n = n/size计算出异常值,导致分配超大内存,触发系统OOM Killer杀死进程。

修复步骤

  1. 修正矩阵B的初始化与分发

    • 仅根进程(rank=0)分配完整的B矩阵内存并初始化,通过MPI_Bcast广播给所有进程。
    • 移除未使用的A指针和subB变量,消除冗余逻辑。
  2. 确保拓扑与进程数匹配

    • 运行时必须指定8个进程:mpiexec -n 8 ./lab。
    • 代码中添加进程数检查,若进程数不等于2^N则报错退出。
  3. 添加内存分配校验

    • 每个malloc/calloc调用后检查返回值,分配失败则打印错误并调用MPI_Abort退出。
  4. 修复资源泄漏

    • 释放自定义MPI数据类型和通信子,避免资源泄漏。
  5. 修正矩阵乘法逻辑

    • 确保乘法循环中遍历完整的矩阵维度,保证计算逻辑正确。

完整修复代码

// 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;
}

运行说明

  1. 编译:mpic++ -o lab lab.cpp
  2. 运行:mpiexec -n 8 ./lab(必须指定8个进程,匹配超立方体拓扑要求)

内容的提问来源于stack exchange,提问作者Vlad H

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 22:47:05