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

  1. ompi_mpi_errors_are_fatal_comm_handler(NULL, NULL, message);

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

  1. #ifndef OMPI_ERRHANDLER_PREDEFINED_H
  2. #define OMPI_ERRHANDLER_PREDEFINED_H
  3.  
  4. #include "ompi_config.h"
  5.  
  6. struct ompi_communicator_t;
  7. struct ompi_file_t;
  8. struct ompi_win_t;
  9.  
  10. /**
  11. * Handler function for MPI_ERRORS_ARE_FATAL //---------------看到了吗?fatal_error
  12. */
  13. OMPI_DECLSPEC void ompi_mpi_errors_are_fatal_comm_handler(struct ompi_communicator_t **comm,
  14. int *error_code, ...);
  15. OMPI_DECLSPEC void ompi_mpi_errors_are_fatal_file_handler(struct ompi_file_t **file,
  16. int *error_code, ...);
  17. OMPI_DECLSPEC void ompi_mpi_errors_are_fatal_win_handler(struct ompi_win_t **win,
  18. int *error_code, ...);
  19.  
  20. /**
  21. * Handler function for MPI_ERRORS_RETURN //---------------- error_return
  22. */
  23. OMPI_DECLSPEC void ompi_mpi_errors_return_comm_handler(struct ompi_communicator_t **comm,
  24. int *error_code, ...);
  25. OMPI_DECLSPEC void ompi_mpi_errors_return_file_handler(struct ompi_file_t **file,
  26. int *error_code, ...);
  27. OMPI_DECLSPEC void ompi_mpi_errors_return_win_handler(struct ompi_win_t **win,
  28. int *error_code, ...);
  29.  
  30. #endif /* OMPI_ERRHANDLER_PREDEFINED_H */

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

  1. void ompi_mpi_errors_are_fatal_comm_handler(struct ompi_communicator_t **comm,
  2. int *error_code, ...)
  3. {
  4. char *name;
  5. struct ompi_communicator_t *abort_comm;
  6. va_list arglist;
  7.  
  8. va_start(arglist, error_code);
  9.  
  10. if (NULL != comm) {
  11. name = (*comm)->c_name;
  12. abort_comm = *comm;
  13. } else {
  14. name = NULL;
  15. abort_comm = NULL;
  16. }
  17. backend_fatal("communicator", abort_comm, name, error_code, arglist);
  18. va_end(arglist);
  19. }

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

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

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

  1. static const char FUNC_NAME[] = "MPI_Init";

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

  1. static void backend_fatal(char *type, struct ompi_communicator_t *comm,
  2. char *name, int *error_code,
  3. va_list arglist)
  4. {
  5. /* We only want aggregation while the rte is initialized */
  6. if (ompi_rte_initialized) {
  7. backend_fatal_aggregate(type, comm, name, error_code, arglist);
  8. } else {
  9. backend_fatal_no_aggregate(type, comm, name, error_code, arglist);
  10. }
  11.  
  12. /* In most instances the communicator will be valid. If not, we are either early in
  13. * the initialization or we are dealing with a window. Thus, it is good enough to abort
  14. * on MPI_COMM_SELF, the error will propagate.
  15. */
  16. if (comm == NULL) {
  17. comm = &ompi_mpi_comm_self.comm;
  18. }
  19.  
  20. if (NULL != error_code) {
  21. ompi_mpi_abort(comm, *error_code);
  22. } else {
  23. ompi_mpi_abort(comm, 1);
  24. }
  25. }

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

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

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

  1. // 目前传入的参数是:  &ompi_mpi_comm_self.comm, 1
    int
  2. ompi_mpi_abort(struct ompi_communicator_t* comm,
  3. int errcode)
  4. {
      // 1. 我们要看的第一部分代码: 防止递归,获取节点名称
  5. char *host, hostname[OPAL_MAXHOSTNAMELEN];
  6. pid_t pid = 0;
  7.  
  8. /* Protection for recursive invocation */
  9. if (have_been_invoked) {
  10. return OMPI_SUCCESS;
  11. }
  12. have_been_invoked = true;
  13.  
  14. /* If MPI is initialized, we know we have a runtime nodename, so
  15. use that. Otherwise, call gethostname. */
      // 疑问2: rte到底是什么?这个估计后续的深入了解会接触的更多,可能是runtime environment
  16. if (ompi_rte_initialized) {
        // host代表的也就是rank, 存储在 ompi_process_info 这个结构体中, ————疑问3
  17. host = ompi_process_info.nodename;
  18. } else {
  19. gethostname(hostname, sizeof(hostname));
  20. host = hostname;
  21. }
  22. pid = getpid();

  23.   // 2. 我们要看的第二部分代码: 打印函数调用堆栈
  24. /* Should we print a stack trace? Not aggregated because they
  25. might be different on all processes. */
  26. if (opal_abort_print_stack) {
  27. char **messages;
  28. int len, i;
  29.  
  30. if (OPAL_SUCCESS == opal_backtrace_buffer(&messages, &len)) {
  31. for (i = 0; i < len; ++i) {
  32. fprintf(stderr, "[%s:%d] [%d] func:%s\n", host, (int) pid,
  33. i, messages[i]);
  34. fflush(stderr);
  35. }
  36. free(messages);
  37. } else {
  38. /* This will print an message if it's unable to print the
  39. backtrace, so we don't need an additional "else" clause
  40. if opal_backtrace_print() is not supported. */
  41. opal_backtrace_print(stderr, NULL, 1);
  42. }
  43. }

  44.   // 3. 第三部分代码: abort之前的自旋等待
  45. /* Should we wait for a while before aborting? */
  46.  
  47. if (0 != opal_abort_delay) {
  48. if (opal_abort_delay < 0) {
  49. fprintf(stderr ,"[%s:%d] Looping forever (MCA parameter opal_abort_delay is < 0)\n",
  50. host, (int) pid);
  51. fflush(stderr);
  52. while (1) {
  53. sleep(5);
  54. }
  55. } else {
  56. fprintf(stderr, "[%s:%d] Delaying for %d seconds before aborting\n",
  57. host, (int) pid, opal_abort_delay);
  58. do {
  59. sleep(1);
  60. } while (--opal_abort_delay > 0);
  61. }
  62. }
  63.   
      // 4. 第四部分: RTE未初始化的情况下,看ompi_mpi_finalized的情况是哪种
  1. /* If the RTE isn't setup yet/any more, then don't even try
  2. killing everyone. Sorry, Charlie... */
  3. if (!ompi_rte_initialized) {
  4. 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",
  5. host, (int) pid, ompi_mpi_finalized ?
  6. "after MPI_FINALIZE started" : "before MPI_INIT completed");
  7. _exit(errcode == 0 ? 1 : errcode);
  8. }

  9.    // 5.第五部分: 有了communicator, 干掉进程集————疑问4:那是不是说,如果有一个进程初始化失败,整个进程集都会挂掉呢?
  10. /* If OMPI is initialized and we have a non-NULL communicator,
  11. then try to kill just that set of processes */
  12. if (ompi_mpi_initialized && !ompi_mpi_finalized && NULL != comm) {
  13. try_kill_peers(comm, errcode);      // 疑问 5
  14. }

  15.   // 6. 第六部分: 很少情况会执行到这里,abort运行环境
  16. /* We can fall through to here in a few cases:
  17.  
  18. 1. The attempt to kill just a subset of peers via
  19. try_kill_peers() failed (e.g., as of July 2014, ORTE does
  20. returns NOT_IMPLENTED from orte_rte_abort_peers()).
  21. 2. MPI wasn't initialized, was already finalized, or we got a
  22. NULL communicator.
  23.  
  24. In all of these cases, the only sensible thing left to do is to
  25. kill the entire job. Wah wah. */
  26. ompi_rte_abort(errcode, NULL);        //疑问 6
  27.  
  28. /* Does not return */
  29. }  

为了章节结构好看,本篇就到此,留下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. react系列(六)Redux Saga

    在Redux中常要管理异步操作,目前社区流行的有Redux-Saga.Redux-thunk等.在管理复杂应用时,推荐使用Redux-Saga,它提供了用 generator 书写类同步代码的能力. ...

  2. Swift_属性

    Swift_属性 点击查看源码 class DataImporter { var fileName = "data.txt" init() { print("初始化&qu ...

  3. 『ACM C++』 PTA 天梯赛练习集L1 | 007-011

    真的是忙头晕了,学业.ACM打题.班级活动.自学新东西,哇这充实的大学~ ------------------------------------------------L1-007--------- ...

  4. 新手Linux命令学习

    一.dd命令:1.可以复制文件,2.可以制作ios镜像,简单理解就是备份 常用的参数  if 设置输入文件的名称 of  设置输出文件的名称 bs  设置每个“”块“”大小 count  要复制“块” ...

  5. 登录验证码的生成Java代码

    package example7; import java.awt.Color;import java.awt.Font;import java.awt.Graphics2D;import java. ...

  6. ThinkPHP5.0图片上传生成缩略图实例代码

    很多朋友遇到这样一个问题,图片上传生成缩略图,很多人在本机(win)测试成功,上传到linux 服务器后错误. 我也遇到同样的问题.网上一查,有无数的人说是服务器临时文件目录权限问题. 几经思考后,发 ...

  7. Hive(3)-meta store和hdfs详解,以及JDBC连接Hive

    一. Meta Store 使用mysql客户端登录hadoop100的mysql,可以看到库中多了一个metastore 现在尤其要关注这三个表 DBS表,存储的是Hive的数据库 TBLS表,存储 ...

  8. Python学习手册之数据封装、类方法、静态方法和属性函数

    在上一篇文章中,我们介绍了 Python 的内部方法.操作符重载和对象生命周期,现在我们介绍 Python 的数据封装.类方法.静态方法和属性函数.查看上一篇文章请点击:https://www.cnb ...

  9. R语言爬虫:穿越表单

    使用rvest包实现实现穿越表单以及页面的跳转 formurl <- "http://open.xmu.edu.cn/oauth2/authorize?client_id=1010&a ...

  10. Log4net 配置实例

    首先需要下载并引用Log4net的binary.这一步可以通过在Visual Studio里的Manage Nuget package for solution轻松添加. 第二步是配置config文件 ...