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

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\n", 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)\n",
host, (int) pid);
fflush(stderr);
while (1) {
sleep(5);
}
} else {
fprintf(stderr, "[%s:%d] Delaying for %d seconds before aborting\n",
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!\n",
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 函数

OpenMPI源码剖析2:ompi_mpi_errors_are_fatal_comm_handler函数的更多相关文章

  1. OpenMPI源码剖析1:MPI_Init初探

    OpenMPI的底层实现: 我们知道,OpenMPI应用起来还是比较简单的,但是如果让我自己来实现一个MPI的并行计算,你会怎么设计呢?————这就涉及到比较底层的东西了. 回想起我们最简单的代码,通 ...

  2. 5.2【Linux 内核网络协议栈源码剖析】socket 函数剖析 ☆☆☆

    深度剖析网络协议栈中的 socket 函数,可以说是把前面介绍的串联起来,将网络协议栈各层关联起来. 应用层 FTP SMTP HTTP ... 传输层 TCP UDP 网络层 IP ICMP ARP ...

  3. OpenMPI源码剖析:网络通信原理(二) 如何选择网络协议?

    因为比较常用的是 TCP 协议,所以在 opal/mca/btl/tcp/btl_tcp.h 头文件中找到对应的 struct mca_btl_tcp_component_t { mca_btl_ba ...

  4. OpenMPI源码剖析3:try_kill_peers 和 ompi_rte_abort 函数

    接着上一篇的疑问,我们说道,会执行 try_kill_peers 函数,它的函数定义在 ompi_mpi_abort.c 下: // 这里注释也说到了,主要是杀死在同一个communicator的进程 ...

  5. OpenMPI源码剖析:网络通信原理(一)

    MPI中的网络通信的原理,需要解决以下几个问题: 1. MPI使用什么网络协议进行通信? 2.中央数据库是存储在哪一台机器上? 3.集群中如果有一台机器挂掉了是否会影响其他机器? 参考: https: ...

  6. OpenMPI源码剖析4:rte.h 头文件的说明信息

    上一篇文章中说道,我们在 rte.h 中发现了有价值的说明: 我们一块一块来分析,首先看到第一块,关于 Process name Object: * (a) Process name objects ...

  7. STL源码剖析之_allocate函数

    SGI STL提供的标准std::allocator中的_allocate函数代码如下: template<class T> inline T* _allocate(ptrdiff_t s ...

  8. 菜鸟nginx源码剖析 框架篇(一) 从main函数看nginx启动流程(转)

    俗话说的好,牵牛要牵牛鼻子 驾车顶牛,处理复杂的东西,只要抓住重点,才能理清脉络,不至于深陷其中,不能自拔.对复杂的nginx而言,main函数就是“牛之鼻”,只要能理清main函数,就一定能理解其中 ...

  9. c++ stl源码剖析学习笔记(一)uninitialized_copy()函数

    template <class InputIterator, class ForwardIterator>inline ForwardIterator uninitialized_copy ...

随机推荐

  1. iis服务器php环境 failed to open stream: No such file or directory解决办法

    项目主机用的windows系统,iis服务器:远程连接桌面—>本地资源->映射D盘驱动器,将本地d盘修改后的文件放在远程主机项目目录里,访问报出failed to open stream: ...

  2. HP-UNIX平台修改Oracle processes参数报错:ORA-27154、ORA-27300、ORA-27301、ORA-27302

    OS 版本     :HP-UX B.11.31Oracle版本:11.2.0.4 (RAC) (一)问题描述 最近发现无法连接上数据库,报错信息为“ORA-00020:maximum number ...

  3. iOS之利用runtime,避免可变数组和可变字典为nil或者数组越界导致的崩溃

    NSArray.NSMutableArray.NSDictionary.NSMutableDictionary.是我们的在iOS开发中非常常用的类.当然,在享受这些类的便利的同时,它们也给我们带来一些 ...

  4. mysql学习记录,CASE WHEN THEN ELSE END用法

    记mysql,case when then else end用法 用法1:搜索函数 SELECT r.order_no, r.golds, r.pay_tool, , ) ) END AS price ...

  5. Java线程池的创建详解

    本篇文章主要总结了Java创建线程池的三种方式以及线程池参数的详细说明,对线程池感兴趣的同学可以作为参考学习. 1)通过工具类java.util.concurrent.Executors的静态方法来创 ...

  6. 关于mysql 8.0.13zip包安装

    mysql 8.0.13默认有一个data文件夹,这个文件夹得删了,不然安装服务时候会有日志文件提示报错: Failed to find valid data directory. Data Dict ...

  7. Verilog_Day3

    内容为书中第5章 条件语句 条件语句必须在过程块语句中使用.所谓过程块语句是指由 initial 和 always 语句引导的执行语句集合.除这两种块语句引导的begin_end块中可以编写条件语句外 ...

  8. 实现一个带有指纹加密功能的笔记本(Android)第二部分

    上文基本完成了整个笔记本的笔记功能的实现,接下来记录实现指纹识别加密以及一些小注意事项. 首先判断该手机是否具备指纹识别的硬件功能和用户是否开启指纹识别. public boolean isFinge ...

  9. CSS-cascading stle sheets

    CSS-cascading stle sheets 1.      CSS 什么是CSS?CSS 指层叠样式表 (Cascading Style Sheets) 样式定义如何显示 HTML 元素 样式 ...

  10. Java 高级应用编程 第二章 集合

    一.Java 中的集合类 1.集合概述 Java中集合类是用来存放对象的 集合相当于一个容器,里面包容着一组对象 —— 容器类 其中的每个对象作为集合的一个元素出现 Java API提供的集合类位于j ...