2

I'm in the process of modifying an existing application, where I would like to spawn a dynamically created bash script. I created a simple wrapper routine which takes the name of the bash script as an argument. In the wrapper, the script is spawned by MPI_Comm_spawn. Directly after, the wrapper calls MPI_Finalize, which is executed before the scripts have finished:

#include "mpi.h"
#include <stdlib.h>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    char *script = argv[1];
    int maxProcs = 2, myRank;
    MPI_Comm childComm;
    int spawnError[maxProcs];

    // Initialize
    argv[1] = NULL;
    MPI_Init(&argc, &argv);

    // Rank of parent process
    MPI_Comm_rank(MPI_COMM_WORLD, &myRank);    

    // Spawn application    
    MPI_Comm_spawn(script, MPI_ARGV_NULL, maxProcs, MPI_INFO_NULL, myRank, MPI_COMM_SELF, &childComm, spawnError);

    // Finalize
    MPI_Finalize();

    return EXIT_SUCCESS;
}

If I insert

    sleep(10);

right before

    MPI_Finalize ();

everything works fine. Now my question is if it is possible to block execution in the wrapper until the bash script is finished? Also, it would be nice to obtain the return value of the script. Unfortunately, it is not an option to create another wrapper for the script, which communicates with the parent wrapper and executes the bash scripts via a system call because I need to access MPI environment variables from within the script. I hope, I have made things clear enough. Any help would be greatly appreciated!

4

3 回答 3

3

如果您可以控制 bash 脚本的内容,即如果您可以在生成之前将一些内容放入其中,那么一个非常粗略的选择是编写一个包含单行的特殊 MPI 程序MPI_Barrier

#include <mpi.h>

int main (int argc, char **argv)
{
   MPI_Comm parent;

   MPI_Init(&argc, &argv);

   // Obtain an intercommunicator to the parent MPI job
   MPI_Comm_get_parent(&parent);

   // Check if this process is a spawned one and if so enter the barrier
   if (parent != MPI_COMM_NULL)
      MPI_Barrier(parent);

   MPI_Finalize();

   return 0;
}

将该程序编译为具有与主 MPI 程序使用的相同 MPI 分布的任何其他 MPI 程序,并将其命名为waiter. 然后EXIT在 bash 脚本的最开始设置一个陷阱:

#!/bin/bash
trap "/path/to/waiter $*" EXIT
...
# End of the script file

同时修改主程序为:

// Spawn application    
MPI_Comm_spawn(script, MPI_ARGV_NULL, maxProcs, MPI_INFO_NULL, myRank, MPI_COMM_SELF, &childComm, spawnError);

// Wait for the waiters to enter the barrier
MPI_Barrier(childComm);

// Finalize
MPI_Finalize();

重要的waiter是,在陷阱内部调用waiter $*它以便它可以接收 bash 脚本将接收的所有命令行参数,因为一些旧的 MPI 实现将附加参数附加到生成的可执行文件,以便为其提供父连接信息。符合 MPI-2 的实现通常通过环境提供此信息以支持MPI_Init(NULL, NULL).

它的工作方式非常简单:该trap命令指示 shellwaiter在脚本退出时执行。waiter它本身只是简单地与父 MPI 作业建立一个互通器并在屏障上等待。一旦所有生成的脚本都完成了,它们都将启动等待者进程作为退出陷阱的一部分,并且障碍将被解除。

如果您无法修改脚本,则只需创建一个调用实际脚本的包装器脚本并将服务员放入包装器中。

经过测试并与 Open MPI 和 Intel MPI 配合使用。

于 2013-07-30T18:22:58.303 回答
1

据我所知,没有一种方法可以制造MPI_COMM_SPAWN块,这里通常的解决方案是MPI_BARRIER在产卵者和产卵者之间建立一个。不幸的是,您没有遵循通常的模型,即 MPI 应用程序生成另一个 MPI 应用程序。相反,您只是在运行一堆脚本。为了得到你想要的结果,你可能不得不使用 MPI 以外的东西,或者想办法为你的远程 bash 脚本编写一个 MPI 包装器。

于 2013-07-30T16:32:50.283 回答
0

为什么不使用一个子 MPI 应用程序来实际使用 fork plus exec 执行脚本。脚本名称可以作为参数传递给使用 MPI_Comm_spawn 或 MPI_Comm_spawn_multiple 创建的子代。然后,这些孩子通过等待来等待脚本完成,或者如果出现错误,则通过处理 SIGCHLD。脚本完成后,您可以在父 MPI 进程和子 MPI 进程之间输入屏障,然后通过调用 MPI_Finalize 终止。

子程序将类似于 Hristo Iliev 提出的程序:

#include <mpi.h>

int main (int argc, char **argv){
   MPI_Comm parent;
   MPI_Init(&argc, &argv);
   MPI_Comm_get_parent(&parent);

   pid = fork();  
   if (pid < 0) { // error while forking
       exit (-1);
   } else if (pid == 0) { // child
       execvp(<nome of the script parsed from parameters in argv or other means>);
   } else { // parent
       wait(<pid of the child>); // there are non-blocking alternatives if needed
   }

   if (parent != MPI_COMM_NULL){
      MPI_Barrier(parent);
   }

   MPI_Finalize();

   return 0;
}

父程序简单地发出 spawn(如果有单个脚本名称)或 spawn_multiple(如果每个生成的 MPI 进程都有不同的脚本名称),然后使用生成的子进程间通信器作为输出参数的屏障MPI 生成操作。

于 2013-08-01T12:35:32.080 回答