1

我在调用MPI_FINALIZE()Fortran 90 程序时遇到分段错误。虽然代码相当广泛,但我将发布伪代码并查看它是否引发任何标志。我有一种预感(但还没有尝试过)这可能是由于没有释放数组引起的?但是我不确定 - 在 Fortran 90 中未能解除分配数组会导致调用期间出现分段错误MPI_FINALIZE吗?

if(<rank 0>) then
  do iat = 1,natoms
    do il = 0, LMAX
      do im = -il,il
        <mpi_recv "rank_rdy"> ! find out which rank is ready for (at,l,m)
        <mpi_send "(iat,il,im)"> ! send (at,l,m) to the rank asking for it
      enddo
    enddo
  enddo
else ! other ranks send a 'ready' signal and recieve the (at,l,m) to optimize
  if(<rank 0 is not finished processing (at,l,m)'s>)
    <mpi_send "my_rank"> ! tell rank 0 that i am ready to receive
    <mpi_recv "(iat,il,im)"> ! recieve (at,l,m) from rank 0
    call optimize(iat,il,im) ! do work on (at,l,m)
  endif
endif

if(<rank 0>)
  <read temp files created by other ranks>
  <write temp files to one master file>
endif

print*, 'calling finalize'

call MPI_BARRIER(MPI_COMM_WORLD, ierr)
call MPI_FINALIZE()

现在在输出中,除了与此问题无关的其他信息外,我还得到以下信息:

 calling finalize
 calling finalize
 calling finalize
 calling finalize
 calling finalize
 calling finalize

=====================================================================================
=   BAD TERMINATION OF ONE OF YOUR APPLICATION PROCESSES
=   EXIT CODE: 11
=   CLEANING UP REMAINING PROCESSES
=   YOU CAN IGNORE THE BELOW CLEANUP MESSAGES
=====================================================================================
APPLICATION TERMINATED WITH THE EXIT STRING: Segmentation fault (signal 11)

即使我不打电话,我也会遇到同样的问题,MPI_BARRIER但我认为这可能会有所帮助。请注意,每个等级中都使用了一些数组,我不会费心解除分配,因为我在整个程序中都使用它们,所以我不担心内存泄漏或任何事情。MPI_FINALIZE()是否有可能由于在没有释放内存的情况下被调用而发生此段错误?

我将自己探索更多,但我想发布这个问题有几个原因:

  1. 想知道这是否是调用时的已知问题MPI_FINALIZE()

  2. 知道调用MPI_FINALIZE(). 在内部,导致此段错误的原因是什么?

  3. 我在网上搜索了高低,没有发现任何关于这个问题的信息,所以对于后人来说,这可能是一个在网上回答的好问题。

编辑:我忘了提到这一点,但是在串行运行时我无法复制这个问题。显然,我不做(at,l,m)串行分配。唯一的过程只是简单地遍历所有组合并逐一优化它们。我没有,但是解除分配我认为可能导致 MPI 问题的数组,并且我仍然没有遇到段错误。

4

1 回答 1

5

如果可用,应始终使用 Fortran 90 MPI 接口,而不是旧的 FORTRAN 77 接口。那就是你应该永远

USE mpi

代替

INCLUDE 'mpif.h'

两者的区别在于 Fortran 90 接口将所有 MPI 子例程放在一个模块中,因此会生成显式接口。这允许编译器在调用中进行参数检查,并在您省略参数时发出错误信号。

在 Fortran 的调用约定中,所有参数都按地址传递,而与它们的类型无关。这允许编译器生成对函数和子例程的正确调用,而无需像在 C 中那样需要原型。但这也意味着人们可以自由地将参数传递给预期INTEGER的数组,并且几乎所有 FORTRAN 77 编译器都会愉快地编译这样的代码或一个REAL可以传递比预期更少/更多的参数。有一些外部工具,通常以 C 工具的名称称为linterlint,可以解析整个源代码树,并可以查明此类错误以及编译器不关心的许多其他错误。为 Fortran 进行此类静态代码分析的工具之一是flint. Fortran 90 添加了接口以弥补 Fortran 容易出错的特性。

使用比预期更少的参数调用 Fortran 子例程可能会产生许多不同的不良影响,具体取决于架构,但在大多数情况下会导致崩溃,特别是如果省略的参数是输出参数。被调用的函数不知道传递的参数更少——它只是查看它的地址应该在哪里,并获取它在那里找到的任何地址。作为ierr是一个输出参数,将发生对该地址的写入。该地址很有可能不会指向与映射内存相对应的虚拟地址,并且操作系统将提供严重的分段错误。即使地址指向用户分配的内存中的某个位置,结果也可能是某些控制结构中的重要值被覆盖。如果这没有发生,那么有调用约定,其中被调用者清理堆栈帧 - 在这种情况下,堆栈指针将不正确地递增并且返回地址将与正确的完全不同,这几乎肯定会导致跳转到不可执行(甚至非映射)内存并再次导致分段错误。

于 2012-07-19T19:31:58.117 回答