• OpenMPI源码剖析2:ompi_mpi_errors_are_fatal_comm_handler函数


    上一篇文章说道,初始化失败会有一个函数调用: 

    ompi_mpi_errors_are_fatal_comm_handler(NULL, NULL, message);

    所以这里简单地进入了 ompi_mpi_errors_are_fatal_comm_handler 函数:
    看到其头文件 errhandler_predefined.h :

    #ifndef OMPI_ERRHANDLER_PREDEFINED_H
    #define OMPI_ERRHANDLER_PREDEFINED_H
    
    #include "ompi_config.h"
    
    struct ompi_communicator_t;
    struct ompi_file_t;
    struct ompi_win_t;
    
    /**
     * Handler function for MPI_ERRORS_ARE_FATAL								//---------------看到了吗?fatal_error
     */
    OMPI_DECLSPEC void ompi_mpi_errors_are_fatal_comm_handler(struct ompi_communicator_t **comm,
    					    int *error_code, ...);
    OMPI_DECLSPEC void ompi_mpi_errors_are_fatal_file_handler(struct ompi_file_t **file,
    					    int *error_code, ...);
    OMPI_DECLSPEC void ompi_mpi_errors_are_fatal_win_handler(struct ompi_win_t **win,
    					    int *error_code, ...);
    
    /**
     * Handler function for MPI_ERRORS_RETURN									//---------------- error_return
     */
    OMPI_DECLSPEC void ompi_mpi_errors_return_comm_handler(struct ompi_communicator_t **comm,
                                       int *error_code, ...);
    OMPI_DECLSPEC void ompi_mpi_errors_return_file_handler(struct ompi_file_t **file,
                                       int *error_code, ...);
    OMPI_DECLSPEC void ompi_mpi_errors_return_win_handler(struct ompi_win_t **win,
                                       int *error_code, ...);
    
    
    #endif /* OMPI_ERRHANDLER_PREDEFINED_H */
    

     跳去它的实现文件 errhandler_predefined.c 中看对应函数: 

    void ompi_mpi_errors_are_fatal_comm_handler(struct ompi_communicator_t **comm,
                                                int *error_code, ...)
    {
      char *name;
      struct ompi_communicator_t *abort_comm;
      va_list arglist;
    
      va_start(arglist, error_code);
    
      if (NULL != comm) {
          name = (*comm)->c_name;
          abort_comm = *comm;
      } else {
          name = NULL;
          abort_comm = NULL;
      }
      backend_fatal("communicator", abort_comm, name, error_code, arglist);
      va_end(arglist);
    }
    

     映入眼帘的是  ompi_communicator_t 类,MPI中的通信子应该就是通过这个类来实现的,后期要重点学习

     这里涉及到不变参数列表的知识,参考此处:  http://www.cnblogs.com/hanyonglu/archive/2011/05/07/2039916.html

    放到此处的具体场景,也就是arglist只有message一个变量,其实也就是:

    static const char FUNC_NAME[] = "MPI_Init";

    这里做了一些处理之后,调用 backend_fatal 函数,这是在本文件中的一个局部函数, 它做了什么呢? :

    static void backend_fatal(char *type, struct ompi_communicator_t *comm,
                              char *name, int *error_code,
                              va_list arglist)
    {
        /* We only want aggregation while the rte is initialized */
        if (ompi_rte_initialized) {
            backend_fatal_aggregate(type, comm, name, error_code, arglist);
        } else {
            backend_fatal_no_aggregate(type, comm, name, error_code, arglist);
        }
    
        /* In most instances the communicator will be valid. If not, we are either early in
         * the initialization or we are dealing with a window. Thus, it is good enough to abort
         * on MPI_COMM_SELF, the error will propagate.
         */
        if (comm == NULL) {
            comm = &ompi_mpi_comm_self.comm;
        }
    
        if (NULL != error_code) {
            ompi_mpi_abort(comm, *error_code);
        } else {
            ompi_mpi_abort(comm, 1);
        }
    }
    

      backend_fatal_aggregate 函数是和聚合有关的,我们先跳过。————留下疑点1

    因为我们传入的 error_code 参数为NULL, 最后程序会进入  ompi_mpi_abort(comm, 1);

    它在 mpiruntime.h 头文件中,实现在 ompi_mpi_abort.c 文件中,贴上它的函数定义代码:

    // 目前传入的参数是:   &ompi_mpi_comm_self.comm, 1
    int ompi_mpi_abort(struct ompi_communicator_t* comm, int errcode) {
      // 1. 我们要看的第一部分代码: 防止递归,获取节点名称 char *host, hostname[OPAL_MAXHOSTNAMELEN]; pid_t pid = 0; /* Protection for recursive invocation */ if (have_been_invoked) { return OMPI_SUCCESS; } have_been_invoked = true; /* If MPI is initialized, we know we have a runtime nodename, so use that. Otherwise, call gethostname. */
      // 疑问2: rte到底是什么?这个估计后续的深入了解会接触的更多,可能是runtime environment if (ompi_rte_initialized) {
        // host代表的也就是rank, 存储在 ompi_process_info 这个结构体中, ————疑问3 host = ompi_process_info.nodename; } else { gethostname(hostname, sizeof(hostname)); host = hostname; } pid = getpid();
      // 2. 我们要看的第二部分代码: 打印函数调用堆栈 /* Should we print a stack trace? Not aggregated because they might be different on all processes. */ if (opal_abort_print_stack) { char **messages; int len, i; if (OPAL_SUCCESS == opal_backtrace_buffer(&messages, &len)) { for (i = 0; i < len; ++i) { fprintf(stderr, "[%s:%d] [%d] func:%s ", host, (int) pid, i, messages[i]); fflush(stderr); } free(messages); } else { /* This will print an message if it's unable to print the backtrace, so we don't need an additional "else" clause if opal_backtrace_print() is not supported. */ opal_backtrace_print(stderr, NULL, 1); } }
      // 3. 第三部分代码: abort之前的自旋等待 /* Should we wait for a while before aborting? */ if (0 != opal_abort_delay) { if (opal_abort_delay < 0) { fprintf(stderr ,"[%s:%d] Looping forever (MCA parameter opal_abort_delay is < 0) ", host, (int) pid); fflush(stderr); while (1) { sleep(5); } } else { fprintf(stderr, "[%s:%d] Delaying for %d seconds before aborting ", host, (int) pid, opal_abort_delay); do { sleep(1); } while (--opal_abort_delay > 0); } }   
      // 4. 第四部分: RTE未初始化的情况下,看ompi_mpi_finalized的情况是哪种
        /* If the RTE isn't setup yet/any more, then don't even try
           killing everyone.  Sorry, Charlie... */
        if (!ompi_rte_initialized) {
            fprintf(stderr, "[%s:%d] Local abort %s completed successfully, but am not able to aggregate error messages, and not able to guarantee that all other processes were killed!
    ",
                    host, (int) pid, ompi_mpi_finalized ?
                    "after MPI_FINALIZE started" : "before MPI_INIT completed");
            _exit(errcode == 0 ? 1 : errcode);
        }
    
       // 5.第五部分: 有了communicator, 干掉进程集————疑问4:那是不是说,如果有一个进程初始化失败,整个进程集都会挂掉呢? /* If OMPI is initialized and we have a non-NULL communicator, then try to kill just that set of processes */ if (ompi_mpi_initialized && !ompi_mpi_finalized && NULL != comm) { try_kill_peers(comm, errcode);      // 疑问 5 }
      // 6. 第六部分: 很少情况会执行到这里,abort运行环境 /* We can fall through to here in a few cases: 1. The attempt to kill just a subset of peers via try_kill_peers() failed (e.g., as of July 2014, ORTE does returns NOT_IMPLENTED from orte_rte_abort_peers()). 2. MPI wasn't initialized, was already finalized, or we got a NULL communicator. In all of these cases, the only sensible thing left to do is to kill the entire job. Wah wah. */ ompi_rte_abort(errcode, NULL);        //疑问 6 /* Does not return */ }  

      为了章节结构好看,本篇就到此,留下6个疑问,再次重申一下:

    1. backend_fatal_aggregate 函数

    2: rte到底是什么?这个估计后续的深入了解会接触的更多,可能是runtime environment

    3. host代表的也就是rank, 存储在 ompi_process_info 这个结构体中

    4.那是不是说,如果有一个进程初始化失败,整个进程集都会挂掉呢?

    5. try_kill_peers 函数

    6. ompi_rte_abort 函数

  • 相关阅读:
    第六周作业
    第四周作业(登录验证码)
    JSP第二次作业
    JSP第一次作业求1-100间的素数
    软件测试课堂作业
    安卓作业(购物商城)
    安卓作业
    Android 简易计算器
    jsp第七次作业
    JSP第六次作业
  • 原文地址:https://www.cnblogs.com/HelloGreen/p/8747538.html
Copyright © 2020-2023  润新知