MPI_Scatterv (c) 会给出segmentation fault

MPI_Scatterv (c) will give segmentation fault

我已经构建了一个相当简单的 c 代码来读取 pgm 图像,将其拆分为不同的部分并将其发送到各个内核以对其进行细化。

为了解决一些细节问题(每个核心必须访问比它需要写入的更大的图像区域),我不能简单地分割图像,但我首先必须创建一个数组我在其中添加前面提到的边距。

作为一个简单的例子:图像是 1600x1200(宽 x 高),我有 2 个核心,我想访问以像素为中心的 3x3 区域,然后我将这个图像水平线分割成水平线然后细分将是 -> 第一个核心获取从 0 到 6011600 的像素,第二个核心获取从 5091600 到 1200*1600 的像素。

现在,我相信我在我的程序中实现它的方式没有任何问题,但我仍然得到这个错误:

[ct1pt-tnode003:22389:0:22389] Caught signal 11 (Segmentation fault: address not mapped to object at address 0x7ffe7f60ead8)
==== backtrace (tid:  22389) ====
 0 0x000000000004ee05 ucs_debug_print_backtrace()  ???:0
 1 0x0000000000402624 main()  ???:0
 2 0x0000000000022505 __libc_start_main()  ???:0
 3 0x0000000000400d99 _start()  ???:0

这是我的代码:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <math.h>
#include <time.h>
#include "testlibscatter.h"
#include <mpi.h>

#define MSGLEN 2048


int main(int argc, char *argv[]){

MPI_Init(&argc, &argv);

int m = atoi(argv[1]), n = atoi(argv[2]), kern_type = atoi(argv[3]);
double kernel[m*n];
int i_rank, ranks;
int param, symm;

MPI_Comm_rank( MPI_COMM_WORLD, &i_rank);
MPI_Comm_size( MPI_COMM_WORLD, &ranks);

int xsize, ysize, maxval;
xsize = 0;
ysize = 0;
maxval = 0;

void * ptr;

switch (kern_type){
    case 1:
    meankernel(m, n, kernel);
    break;
    case 2:
    weightkernel(m, n, param, kernel);
    break;
    case 3:
    gaussiankernel(m, n, param, symm, kernel);
    break;
}

if (i_rank == 0){
    read_pgm_image(&ptr, &maxval, &xsize, &ysize, "check_me2.pgm");
}


MPI_Bcast(&xsize, 1, MPI_INT, 0, MPI_COMM_WORLD);
MPI_Bcast(&ysize, 1, MPI_INT, 0, MPI_COMM_WORLD);
MPI_Bcast(&maxval, 1, MPI_INT, 0, MPI_COMM_WORLD);

int flo, start, end, i;
flo = floor(ysize/ranks);

int first, last;

first = start - (m - 1)/2;
last = end + (m - 1)/2;

if (start == 0){
    first = 0;
}
if (end == ysize){
    last = ysize;
}

int sendcounts[ranks];
int displs[ranks];

int first2[ranks];
int last2[ranks];
int c_start2[ranks];
int c_end2[ranks];

int num;
num = (ranks - 1) * (m-1);
printf("num is %d\n", num);

unsigned short int bigpic[xsize*(ysize + num)];


if (i_rank == 0){
    for(i = 0; i < ranks; i++){
        c_start2[i] = i * flo;
        c_end2[i] = (i + 1) * flo; 
        if ( i == ranks - 1){
            c_end2[i] = ysize;
        }
        first2[i] = c_start2[i] - (m - 1)/2;
        last2[i] = c_end2[i] + (m - 1)/2;
        if (c_start2[i] == 0){
            first2[i] = 0;
        }
        if (c_end2[i] == ysize){
            last2[i] = ysize;
        }
        sendcounts[i] = (last2[i] - first2[i]) * xsize; 
    }

    int i, j, k, index, index_disp = 0;
    index = 0;
    displs[0] = 0;

    for (k = 0; k < ranks; k++){
        for (i = first2[k]*xsize; i < last2[k]*xsize; i++){
            bigpic[index] = ((unsigned short int *)ptr)[i];
            index++;
        }
        printf("%d\n", displs[index_disp]);
        index_disp++;
        displs[index_disp] = index;
    }

}

MPI_Bcast(displs, ranks, MPI_INT, 0, MPI_COMM_WORLD);
MPI_Bcast(sendcounts, ranks, MPI_INT, 0, MPI_COMM_WORLD);

unsigned short int minipic[xsize*(last-first)];
MPI_Barrier(MPI_COMM_WORLD);
MPI_Scatterv(&bigpic[0], sendcounts, displs, MPI_UNSIGNED_SHORT, minipic, (last-first)*xsize, MPI_UNSIGNED_SHORT, 0, MPI_COMM_WORLD);

MPI_Finalize();
}

函数内核只是 returns 一个 m*n 的数组加倍来编辑图像,而 read_pgm_image returns 一个带有读取图像值的空指针。 我试过打印 bigpic 的值,它们没有问题。

在此处显示的代码中,startendfirstlast 的计算中使用未初始化:

int flo, start, end, i;
         ~~~~~~~~~~
flo = floor(ysize/ranks);

int first, last;

first = start - (m - 1)/2; // <---- start has a random value here
last = end + (m - 1)/2;    // <---- end has a random value here

如果值非常大,minipic 的大小可能会大于堆栈大小:

unsigned short int minipic[xsize*(last-first)];
                                  ^^^^^^^^^^ random (possibly large) value

这确实是原因的有力迹象是,故障地址 0x7ffe7f60ead8 非常接近虚拟地址 space 正数部分的末尾,即大多数 64 位操作系统分配主线程的堆栈区域。

始终使用 -Wall 进行编译,以便从编译器获取尽可能多的诊断消息。