在超级计算机上从MPI程序获取各进程IP地址的方法
嘿,刚好能帮到你!你的MPI程序已经能正常输出进程rank了,要加上每个进程的IP地址其实不难——毕竟MPI本身没有直接提供获取IP的API,咱们得借助操作系统的工具或者系统调用来实现,下面给你两种实用的方案,适配超算的Linux环境:
方案一:通过Shell命令获取IP(纯Fortran实现)
这种方法不用额外写其他语言代码,直接在Fortran里调用Linux的系统命令来获取IP,适合快速实现。我们可以用hostname -I命令(或者更精准的IP筛选命令)获取节点IP,把输出临时存到文件里再读入程序:
program hello include 'mpif.h' integer my_pe_num, errcode, iostat character(len=256) :: ip_addr, hostname integer :: len_host call MPI_INIT(errcode) call MPI_COMM_RANK(MPI_COMM_WORLD, my_pe_num, errcode) ! 先获取当前节点的主机名(可选,但能辅助验证) call MPI_GET_PROCESSOR_NAME(hostname, len_host, errcode) hostname = hostname(1:len_host) ! 截断多余的空格 ! 执行shell命令把IP写入临时文件,每个进程用自己的rank命名文件避免冲突 call execute_command_line('hostname -I > /tmp/ip_'//trim(str(my_pe_num))//'.txt', wait=.true.) ! 读取临时文件里的IP地址 open(unit=10, file='/tmp/ip_'//trim(str(my_pe_num))//'.txt', status='old', iostat=iostat) if (iostat == 0) then read(10, '(A)') ip_addr close(10) ! 清理临时文件 call execute_command_line('rm /tmp/ip_'//trim(str(my_pe_num))//'.txt', wait=.true.) else ip_addr = 'Unknown IP' end if ! 输出rank和IP print *, 'Hello from rank ', my_pe_num, ', IP address: ', trim(ip_addr) call MPI_FINALIZE(errcode) contains ! 辅助函数:把整数转成字符串,用于命名临时文件 function str(i) result(s) integer, intent(in) :: i character(len=20) :: s write(s, '(I0)') i s = adjustl(s) end function str end program hello
说明
hostname -I会输出节点的所有IPv4地址,如果你需要特定网卡(比如以太网)的IP,可以替换命令为:ip addr show eth0 | grep inet | awk '{print $2}' | cut -d/ -f1(根据超算节点的网卡名称调整,比如可能是ib0、enp0s3等)- 临时文件用进程rank命名,避免多个进程写同一个文件导致冲突
- 这个方法依赖Linux的shell命令,超算环境一般都支持
方案二:通过C系统调用获取IP(混合编程,更高效)
如果觉得调用shell命令和临时文件不够高效,可以用C语言的getaddrinfo系统调用直接获取IP,然后在Fortran里调用这个C函数——这种方式不依赖外部命令,速度更快:
第一步:写C语言的IP获取函数(getip.c)
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <unistd.h> void get_ip_address(char *ip, int max_len) { char hostname[256]; struct addrinfo hints, *res, *p; int status; // 获取当前节点的主机名 if (gethostname(hostname, sizeof(hostname)) != 0) { strncpy(ip, "Unknown Hostname", max_len); return; } // 设置addrinfo参数,只获取IPv4地址 memset(&hints, 0, sizeof hints); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; // 通过主机名获取地址信息 if ((status = getaddrinfo(hostname, NULL, &hints, &res)) != 0) { strncpy(ip, gai_strerror(status), max_len); return; } // 遍历地址列表,取第一个IPv4地址 for(p = res; p != NULL; p = p->ai_next) { char ipstr[INET_ADDRSTRLEN]; inet_ntop(p->ai_family, &((struct sockaddr_in*)p->ai_addr)->sin_addr, ipstr, sizeof ipstr); strncpy(ip, ipstr, max_len); break; } freeaddrinfo(res); // 释放内存 }
第二步:修改Fortran程序调用C函数
program hello include 'mpif.h' integer my_pe_num, errcode character(len=256) :: ip_addr ! 声明C函数的接口,让Fortran能调用它 interface subroutine get_ip_address(ip, max_len) bind(C, name='get_ip_address') use, intrinsic :: iso_c_binding character(kind=c_char), intent(out) :: ip(*) integer(kind=c_int), intent(in) :: max_len end subroutine get_ip_address end interface call MPI_INIT(errcode) call MPI_COMM_RANK(MPI_COMM_WORLD, my_pe_num, errcode) ! 调用C函数获取IP地址 call get_ip_address(ip_addr, len(ip_addr, kind=c_int)) ! 去掉C字符串末尾的结束符,整理成Fortran可用的字符串 ip_addr = trim(adjustl(ip_addr)) ! 输出rank和IP print *, 'Hello from rank ', my_pe_num, ', IP address: ', trim(ip_addr) call MPI_FINALIZE(errcode) end program hello
编译方法
用MPI编译器把Fortran和C代码一起编译:
mpif90 hello.f90 getip.c -o hello
说明
- 这个方法直接调用系统API,性能更好,也不会产生临时文件
- 代码里默认取第一个IPv4地址,如果需要其他类型的IP(比如IPv6),可以修改
hints.ai_family为AF_INET6
两种方案都能在超算的批处理环境下正常运行,你可以根据自己的需求选择~
内容的提问来源于stack exchange,提问作者user1544953
相关产品推荐
相关产品推荐

