MPICH2是由Argonne国家实验室维护的开源MPI库,是高性能计算领域使用最为广泛的MPI库之一。本文主要对其中Hydra进程管理框架进行了简要分析,希望可以为相关领域的学者提供帮助。在阅读本文前,读者可以首先参考其官方介绍:http://wiki.mpich.org/mpich/index.php/Hydra_Process_Management_Framework。
Hydra构建了进程管理框架,MPI进程需要经常与该框架进行交互,其交互的语言就是PMI标准,即进程管理接口(Process Management Interface)。因此要全面掌握该框架需要同时了解Hydra和客户端MPI进程两个部分,下面将分别从两个部分展开分析。需要注意的是,文中涉及的内容均以发稿前最新的mpich3.1为准,且使用PMI-1接口。
1. Hydra 进程管理框架
下图是mpich wiki给出的Hydra结构图:
从进程拓扑上看,Hydra由一个运行在服务器上的作业加载程序JLE(Job Launching Executable)和多个运行在不同计算节点上的进程管理代理PMP(Process Manager Proxy)组成。之所以称为PMP是因为它基本上具备了PM(Proxy Manager)的所有功能,代理PM并运行在计算节点上负责具体维护本节点上所有进程的状态。换言之,包括MPI进程在内系统进程拓扑是一个倒树状结构,JLE处于树根,负责在各计算节点上创建进程PMP;PMP在本节点上创建一个或多个MPI进程。JLE对应的执行程序就是我们经常使用的作业加载程序mpiexec.hydra;PMP为hydra_pmi_proxy。
在介绍进程拓扑结构后,我们来看看上图中主要功能模块的作用,按照自底向上的顺序:
1、I/O Demux Engine
Demux的字面意思是“解复用器”。顾名思义,Demux 本质上一个消息管理系统,可以监听多个文件描述符的状态,当文件描述符接收到消息后,回调注册的处理函数。
Demux相关的代码位于src\pm\hydra\tools\demux目录下;其主要函数如下所示:
1)HYDT_dmx_init:根据用户选项指定wait_for_event为HYDT_dmxu_poll_wait_for_event或HYDT_dmxu_select_wait_for_event。
2、Bootstrap
BSCI-BootStrap Control Interface,即引导控制接口,负责程序的加载和状态的维护,该模块定义在hydra\tools\bootstrap目录下。主要包含两个数据结构:struct HYDT_bsci_info 定义主要状态信息;struct HYDT_bsci_fns 定义主要核心函数,这里主要介绍其中有关启动作业的函数
HYD_status(*launch_procs) (char **args, struct HYD_proxy * proxy_list, int *control_fd);
在我们的实验平台中默认使用ssh启动进程,其launsh回调函数指定为HYDT_bscd_common_launch_procs。该函数的主要功能是准备待运行程序的参数,如果发现目标机器为本地节点,则通过fork启动进程;否则通过ssh远程启动进程。两种方式的启动过程均是调用函数HYDU_create_process,差别在于传递该函数的参数不同。例如,在本地创建进程时,参数为:
./examples/hellow
即直接传递MPI程序名称,直接通过fork创建子进程执行程序;
在远程创建进程时,参数为:
/home/wgb/mpich-3.1/install/bin/hydra_pmi_proxy --control-port server:50687 --rmk user --launcher ssh --demux poll --pgid 0 --retries 10 --usize -2 --proxy-id 1
即通过ssh在远程创建进程管理代理hydra_pmi_proxy,参数为本地地址/端口等信息;待执行MPI程序的信息将在后续通知PMP。
BootStrap向外提供下列API接口,可以看出所有函数名均以HYDT_bsci开头:
1)HYD_status HYDT_bsci_init(const char *rmk, const char *launcher, const char *launcher_exec, int enablex, int debug);
2)HYD_status HYDT_bsci_launch_procs(char **args, struct HYD_proxy *proxy_list, int *control_fd);3)HYD_status HYDT_bsci_finalize(void);4)HYD_status HYDT_bsci_wait_for_completion(int timeout);5)HYD_status HYDT_bsci_query_node_list(struct HYD_node **node_list);6)HYD_status HYDT_bsci_query_proxy_id(int *proxy_id);7)HYD_status HYDT_bsci_query_native_int(int *ret);8)HYD_status HYDT_bsci_launcher_XXX_init(void);
3、Process Binding
用于进程与CPU或Memory绑定的模块,貌似是使用Open MPI的hwloc(Portable Hardware Locality)子模块实现的。
4、PM和PMP
PM和PMP完成进程管理的相关功能,PMP执行在计算节点上,完成JLE和MPI进程间的“上传下达”功能。PMP维护了上行通路(upstream)和下行通路(downstream),分别连接了JLE进程和MPI进程。在完成相关初始化后,PMP通过Demux模块等待来自JLE进程和MPI进程的消息。PMP和PM的主要逻辑和数据结构相似。其中enum HYD_pmcd_cmd是两者共用的核心数据结构之一,定义了进程管理需要处理的所有消息类型。
enum HYD_pmcd_cmd {
INVALID_CMD = 0, /* for sanity testing */
/* UI to proxy commands */
PROC_INFO, CKPOINT, PMI_RESPONSE, SIGNAL, STDIN,
/* Proxy to UI commands */
PID_LIST, EXIT_STATUS, PMI_CMD, STDOUT, STDERR, PROCESS_TERMINATED
} cmd;
如注释所示,PROC_INFO至STDIN为JLE向PMP发送的消息类型;PID_LIST至PROCESS_TERMINATED为PMP向JLE发送的消息类型。
PMP的消息处理函数为HYD_pmcd_pmip_control_cmd_cb(pmip_cb.c):
PROC_INFO:完成application进程创建过程。首先为应用进程配置环境变量等准备工作,而后调用HYDU_create_process创建进程。
CKPOINT:检查点功能。
PMI_RESPONSE:处理来自服务器端的请求或相应,根据消息类型调用HYD_pmcd_pmip_pmi_handle中的回调函数,回调函数列表定义在pmi_v1_handle_fns_foo(pmip_pmi_v1.c)中。
STDIN:转发来自服务器端的输入数据到MPI进程。
JLE的消息处理函数为control_cb(pmiserv_cb.c)。
PID_LIST:接受并保存PMP发送的MPI进程号列表
EXIT_STATUS:保存MPI进程的退出状态,并清理PMP
PMI_CMD:调用handle_pmi_cmd处理各类消息,回调函数列表定义在pmi_v1_handle_fns_foo。
STDOUT、STDERR:处理标准输出流和错误流
PROCESS_TERMINATED:处理MPI进程退出
5、User Interface
UI定义了mpiexec.hydra的入口函数,其所有代码定义在hydra/ui目录下。
2. MPICH PMI接口及其实现
MPI应用程序通过PMI接口与PMP交互,PMI-1的接口实现均定义在src/pmi/simple目录下。下面简要介绍其中的主要函数:
PMI_Init:初始化PMI相关变量,例如,PMI_fd,PMI_rank等,PMI_fd用于与PMP通信的描述符,PMI_rank即为MPI进程的rank。
PMI_Get_appnum:获得App序号,从0开始。
PMI_Barrier:同步操作,直至等到JLE返回"barrier_out"消息为止。
PMI_KVS_Get_my_name:从JLB获得进程组标识。
PMI_KVS_Put:发布键-值对。
PMI_KVS_Get:获得键相应的值。
3.PMI交互过程实例
cmd=get_resultrc=0 msg=success value=description#server$port#42070$ifname#192.168.43.10$
这样P0获得了P1的机器名(server)和端口号(42070)和IP地址(192.168.43.10),可以启动发送过程。