news 2026/10/7 16:48:43

Windows下MPI并行计算实战:mpiexec.exe原理与常见问题排查

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Windows下MPI并行计算实战:mpiexec.exe原理与常见问题排查

如果你是在Windows上刚开始接触并行计算,大概率会遇到这种情况:程序装好了、代码编译通过了,可一跑就卡在一条命令上——mpiexec.exe。MPI(Message Passing Interface,消息传递接口)标准本身不复杂,真正劝退新人的往往就是这个进程启动器。它不只是用来启动程序的,节点管理、环境变量注入、进程调度全都在这一层完成。这篇内容,我把关于MPI和mpiexec.exe的底层逻辑、Windows下的安装选型、常用参数、跨节点配置以及各种报错原因一次讲清楚,适合刚入门并行计算、或者正在Windows上折腾MPI环境的读者直接照着操作。

1. 为什么一入门就要跟mpiexec.exe打交道

1.1 先搞清楚MPI是什么:消息传递不是多线程

MPI首先是一套标准,而不是某个软件。标准规定了一套消息传递接口:进程如何初始化、如何发送消息、如何接收消息、如何做同步等。真正实现这套标准的库才是你安装的东西——比如MS-MPI、Open MPI、Intel MPI,都属于MPI的具体实现。

在并行编程里,MPI主要解决的是分布式内存并行的问题。什么叫分布式内存?多个进程各自拥有独立的内存地址空间,进程之间不能直接读写对方的内存,必须通过消息来交换数据。这跟OpenMP有本质不同:OpenMP是共享内存模型,多个线程共享同一个进程的地址空间,通过共享变量通信,编译器帮你做同步。

你可以这样理解两者的分工:假如让你和三个同事一起编辑同一份文档,共享内存的方式是你们坐在同一张桌子前,谁都能直接改这文档;MPI的方式是你们各拿一份文档副本,自己的那份自己改,改完再把必要的内容通过邮件发给别人合并。后者看起来麻烦,但好处是你们可以分布在不同的办公楼,甚至不同的城市——这正是MPI能跨机器、跨节点工作的根本原因。

怎么选型?如果一台机器上多核并行,OpenMP更省事;如果你要跨节点、跨机器做大规模科学计算,MPI基本是必修课。实际工程里两者经常配合使用,典型的做法是MPI跨节点加OpenMP节点内混合并行,也就是常说的hybrid programming。

很多初学者会把MPI理解成多线程库,这是最常见的一个误解。MPI的进程模型和线程模型差异很大:每个MPI进程都有一套完整独立的运行环境,有自己独立的变量和堆栈,进程之间通过互发消息协调。由于进程间不共享内存,即便某个进程崩溃,其他进程也还能继续运行,只不过会因为没有收到数据而卡住或报错。这种隔离性既是MPI的优点,也是排错时需要多留个心眼的地方。

提示:搞清楚"MPI是标准、mpiexec是实现的一部分"这件事,能帮你少走很多弯路。后面遇到报错时,第一件事是确认你用的是哪个MPI实现,很多问题在不同实现里表现完全不同。

1.2 为什么"跑MPI程序"这件事绕不开mpiexec

MPI标准定义了一个进程管理接口(Process Management Interface),规定了一套启动MPI程序的方式。mpiexec就是这个接口的主流实现——它负责把一份程序拷贝分成N个进程,分别放到规划好的主机上,然后协调它们开始执行。

用大白话说,mpiexec.exe是MPI程序的总调度员。你给它程序路径和进程数量,它负责在目标机器上把N个进程拉起来,给每个进程分配一个全局唯一的rank编号,并建立一个进程间通信需要的初始环境。所有MPI程序,无论实现是哪家,最终都要经过这一步才能跑起来。

所以你会发现,不管用MS-MPI还是Open MPI,启动命令长得几乎一样,原因就在这里:标准规定了启动器叫mpiexec,也规定了它至少要支持类似-n这样的参数。差别在于不同实现对这些参数的扩展程度不同,以及底层使用的进程管理通道不同。理解了这条主线,后面所有的参数和报错你都能自己推出来。

这里还要补充一个背景:MPI标准之所以把启动器也纳入规范,是因为并行程序不是"双击exe就能跑"的普通应用。一个进程不知道自己是第几个进程,也不知道该和谁通信、怎么建立连接。mpiexec相当于把这些问题在进程启动之前都安排好,进程起来后再通过MPI_Init去"报到",拿到自己的身份信息,程序才能正常进入并行逻辑。

2. Windows上先选对MPI实现再动手

2.1 三种主流Windows MPI实现的定位差异

Windows不像Linux那样自带开箱即用的MPI生态,第一步就是选实现。目前常见的选项有三个:MS-MPI、Open MPI和Intel MPI,它们的定位差别很大。

实现定位Windows兼容性适合场景
MS-MPI微软官方实现,源自MPICH原生支持,安装简单Windows上跑MPI的首选,科研、工业界大量使用
Open MPI开源社区主力需要额外依赖,支持相对繁琐跨平台项目、需要大量MPI扩展功能的场景
Intel MPI英特尔基于MPICH封装并做优化原生支持,配置略繁琐追求性能的HPC环境

我的建议很直接:如果只打算在Windows上跑,且没有特殊需要,直接用MS-MPI。它是微软官方维护,安装简单,和Visual Studio配合好,而且不涉及额外的Unix兼容层。Open MPI在Windows上虽然也能跑,但经常会遇到依赖缺失、路径权限、符号链接之类的问题,沟通成本不低。比如Open MPI在Windows上依赖一批类似Unix的工具链,缺了某个程序就可能在启动时报"cannot find lswith command not found",这种错误在很多刚入门的同学眼里完全摸不着头脑。

Intel MPI在Windows HPC环境里很常见,但它比MS-MPI多一个环境初始化步骤,要source一个脚本才能正常使用mpiexec命令。如果你不是Intel全家桶的深度用户,基本不用考虑。

2.2 MS-MPI的安装细节:运行时与SDK的区别

MS-MPI在微软官网分两个安装包:MS-MPI Redistributable(运行时)和MS-MPI SDK(开发包)。很多人在这一步就踩坑了:只装了运行时,然后发现没有mpiexec.exe。其实在MS-MPI 10.x版本之后,mpiexec.exe会随运行时一起安装,但如果你需要头文件(mpi.h)、库文件(msmpi.lib)和示例代码,就必须装SDK。

简单说:如果你是开发人员要自己编译MPI程序,运行时和SDK都建议装上;如果只是跑别人编译好的exe,装运行时基本够了。我个人的做法是开发机上全都装,部署到计算节点时只装运行时,这样既保证开发调试没问题,也避免计算节点多出一堆不必要的文件。

安装完成后默认路径是C:\Program Files\Microsoft MPI\,注意这个目录名带空格,后续写批处理脚本或配置文件时要用引号包住路径。Bin子目录下放的是mpiexec.exe、smpd.exe等核心工具。另外要注意x64和x86版本会分别装到Program Files和Program Files (x86)下,如果你的程序是32位编译的,需要确认环境里指向的是x86版本。

提示:安装MS-MPI时,如果之前装过旧版本,建议先卸载干净再装新版。MS-MPI 8.x和10.x之间的兼容性处理得不算好,混装容易出现"程序请求的MS-MPI版本和系统中的版本不一致"这类莫名其妙的报错。

2.3 如何判断手里的程序是用哪个MPI编译的

跑别人编译好的MPI程序之前,先搞清楚它依赖哪个MPI实现、什么版本。最简单的方式是用依赖查看工具查看exe里加载的dll名称,比如Dependencies.exe或DLL dump。查看标准:

  • msmpi.dll对应MS-MPI
  • libmpi.dll、libopen-pal.dll对应Open MPI
  • impi.dll或impi_win.dll对应Intel MPI

用Dependencies打开程序exe,找到DLL列表,确认它动态链接的是哪个MPI运行库,就能避免"明明装了MPI,程序还是报找不到库"的尴尬。这也是我在实际运维中见过最多的一类问题:环境里装了多个MPI,程序却要特定版本。

判断版本的另一个维度是看dll版本号,尤其在MS-MPI里,dll版本和mpiexec版本必须一致。如果程序依赖msmpi.dll 10.x,但系统里的mpiexec是8.x,运行时会报错。这种错误在启动前很难发现,所以收到别人发来的exe时,先养成检查依赖的习惯,能省很多事。

3. mpiexec.exe常用参数与单机多进程实操

3.1 最常见的参数组合与含义

在Windows下跑MPI程序,先掌握这几个参数就够了:

参数含义示例
-n指定总进程数-n 4
-hosts指定主机列表(数量和主机名)-hosts 2 node01 node02
-env给进程设置环境变量-env OMP_NUM_THREADS 1
-wdir设置工作目录-wdir C:\work
-v输出详细执行信息-v

先说-n的参数值。在MS-MPI里,-n和-np等价,都表示总共要启动的进程数。进程数不是越多越好:它受制于CPU核心数、内存和MPI的实现限制。单机场景下,如果机器是16个物理核,跑-n 16一般是合理的;想跑更高可以,但会有明显性能下降,因为操作系统要不停做上下文切换,Cache命中率也会下滑。

-hosts参数格式稍微反直觉:第一个数字表示后面跟了几个主机名,跟的是主机序列。比如跨两台机器各起4个进程,可以写成:

mpiexec -n 8 -hosts 2 node01 node02 app.exe

这里-n 8和-hosts配合时总进程数会被均分到每个主机上。如果你需要每台机器上进程数不同,MS-MPI支持更细粒度的配置语法,但日常使用中这种均分方式最常见,也最不容易出错。

3.2 一个完整的单机示例:从命令到输出

假设你有一个编译好的MPI程序calc.exe,功能是对一组数据做并行求和。最简单起见,我们按单机四进程来跑:

mpiexec -n 4 calc.exe

程序内部每个进程通过MPI_Init拿到自己的rank,示例代码逻辑如下:

#include <mpi.h> #include <stdio.h> int main(int argc, char** argv) { MPI_Init(&argc, &argv); int rank, size; MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); printf("rank %d of %d: hello\n", rank, size); MPI_Finalize(); return 0; }

运行后输出(顺序不固定,取决于调度):

rank 2 of 4: hello rank 0 of 4: hello rank 3 of 4: hello rank 1 of 4: hello

注意,MPI并不保证输出顺序。很多新手看到输出顺序乱了就觉得程序出问题了,其实这只是各个进程各自打印、通过stdout汇合的结果,顺序天然无保证。如果你需要按rank顺序输出,需要在程序里手动做同步,比如用MPI_Barrier或让rank 0负责汇总后再打印。

提示:在Windows的cmd里跑mpiexec时,如果程序路径中包含空格,必须加引号,例如mpiexec -n 4 "C:\My Apps\calc.exe"。否则mpiexec会报"unable to start application"之类的错误。

3.3 环境变量注入:-env/-genv/-envall

MPI程序经常需要给所有进程统一设置环境变量。最典型的场景是混合编程:用OpenMP做节点内多线程,通过OMP_NUM_THREADS控制每个MPI进程的线程数,防止线程过度订阅拖垮性能。

mpiexec -n 4 -env OMP_NUM_THREADS 2 calc.exe

这样每个MPI进程内部会以2个线程运行OpenMP部分,总线程数是8。如果你不加限制直接跑,OpenMP默认会用满所有核心,多个MPI进程加起来的线程数远超过物理核数,反而出现超订阅、cache thrash,性能极其难看。

-env和-genv的区别需要说清楚:-env只作用于单个主机上的进程,-genv是全局设置,跨节点运行时给所有主机上的所有进程同一个值。日常建议直接用-genv,语义更明确。

在跨节点场景里,环境变量的传递依赖每台机器上的代理进程具备对应的路径。如果你设置的是一个自定义变量的路径值,那么目标机器的代理进程必须能在自己的环境里找到这个路径,否则会在启动时报错。这个问题比较隐蔽,排查时记得把目标机器的环境也检查一遍。

4. 跨节点运行:mpiexec在后台做了什么

4.1 进程管理器的三层结构:mpiexec、SMPD与PMI

跨节点运行是MPI真正发挥价值的地方,但也是初学者最觉得黑盒的部分。先理解MPI进程管理系统的三层结构:

第一层是mpiexec.exe本身,它位于启动节点的用户态,负责解析命令行参数、规划进程布局、收集运行日志。它相当于总指挥。

第二层是每台物理节点上都必须运行的代理进程。MS-MPI对应的是smpd.exe(Session Manager Process Daemon);Open MPI在Linux上走ssh/rsh通道,在Windows上则依赖额外的mpiexec服务。这个代理进程必须常驻在每一台目标节点上,任务是从mpiexec那里接收启动指令,在本地调用系统接口拉起真正的MPI进程。

第三层是PMI(Process Management Interface)协议。MPI进程启动之后,并不天然知道自己到底是第几个进程、谁和它相邻。进程需要通过PMI服务从mpiexec那里获取自己的rank编号、参与进程总数、以及与其他进程建立连接所需的信息。你可以把PMI理解成进程的报到大厅:每个进程去大厅报个到,领一个号码牌(rank),再通过大厅通讯录拿到其他人的联系方式。

注意:MPI程序里对MPI_Init的调用,实际上就是去报到大厅完成身份认领。如果PMI通道出问题,程序往往卡在启动阶段或者直接报"MPI_Init failed"。

4.2 跨主机启动的两种通道机制

跨节点启动远程进程,不同MPI实现走的路不一样。

MS-MPI方式:mpiexec连接目标节点上常驻的smpd服务,由smpd负责在本地派生进程。这种方式的优点是:目标机器上只需要有smpd在跑,不需要配置SSH免密、不需要额外账户权限。缺点是需要提前在每台机器上把smpd服务启动起来,并且要让防火墙放行它监听的端口(不同版本端口不同,可以自行查监听端口确认)。

Open MPI方式:mpiexec通过ssh(或rsh)登录到目标节点,然后在远程执行对应的启动命令。所以Open MPI跑跨节点前,必须先配置好SSH免密登录,否则每次启动都会要密码。这个方式在非Windows环境通用,但调试起来链路更长,权限和路径问题更容易出幺蛾子。

理解这两种通道机制,你就能明白为什么同一个mpiexec命令在微软和开源两套MPI里的报错风格截然不同。MS-MPI报错通常围绕"unable to connect to smpd",Open MPI报错通常是"ssh permissions denied"或"no route to host",排查方向完全不同。

4.3 跨节点运行的最简配置清单

如果你要用MS-MPI跑两台Windows机器,我的建议步骤是这样:

  1. 在两台机器上都安装相同版本的MS-MPI(运行时即可)。
  2. 在两台机器上启动smpd服务,命令是smpd -install然后smpd -start,或直接用服务管理器启动名为"Microsoft MPI"的服务。
  3. 确认防火墙规则放行smpd对应的端口。
  4. 从任意一台机器执行:
    mpiexec -n 4 -hosts 2 win01 win02 calc.exe
    此时总进程数4会被均分到两台机器上。

排查时可以先用-v参数看详细输出,它会告诉你mpiexec正在连接哪台机器、遇到什么错误。这个参数在跨节点调试时基本是必开的。另外补充一点:如果你在两台机器上使用了不同的用户名密码,MS-MPI的smpd可以配置为使用同一套凭据,否则跨节点时可能出现凭据错误。具体配置方式是修改smpd服务项的登录账户为同一个域账户或统一本地账户。

5. Windows下mpiexec.exe实战踩坑清单

5.1 环境变量没有配好导致找不到命令

在cmd里敲mpiexec提示"不是内部或外部命令",十有八九是PATH没配好。MS-MPI安装时理论上会自动配PATH,但如果你装了SDK而没有装运行时,或安装过程被安全软件拦截了部分写入,PATH就漏了。

快速修复方式:

set PATH=C:\Program Files\Microsoft MPI\Bin;%PATH%

或者打开系统环境变量,把C:\Program Files\Microsoft MPI\Bin加进PATH。每次重新开终端都要重新set,比较繁琐,建议直接改系统PATH。

还有一个小细节:现代Windows的msi安装程序默认会把x64版MS-MPI装到Program Files下,把x86版装到Program Files (x86)下。如果你的程序是32位的,需要确认PATH里指向的是x86版本,两个目录下都有mpiexec.exe,选错了会出现"bad image"或"版本不匹配"报错。

5.2 SMPD起不来:服务状态、防火墙与杀毒软件

MS-MPI跨节点启动时报"unable to start smpd"这类错误,最常见三个原因。

第一个是smpd服务未启动。在Windows服务管理器里找到"Microsoft MPI",看它是不是运行状态。如果你刚装完MS-MPI,服务可能处于停止状态,需要手动启动或重启机器。

第二个是防火墙拦截。smpd监听固定或动态端口,Windows防火墙默认会弹窗询问是否允许smpd通信,如果你直接点了取消,后面必然会连不上。解决方式是手动添加入站规则,放行smpd.exe,或放行对应端口。

第三个是杀毒软件把smpd当成可疑服务给拦了。MS-MPI的进程间通信方式和某些安全软件的行为检测逻辑冲突,启动失败被吞在后台,外面只看到mpiexec报连接失败。遇到这种问题,先把MS-MPI整个目录加入杀毒白名单,再重启服务,多半能过。

5.3 路径与工作目录的细节问题

MPI程序的工作目录也很容易出问题。mpiexec默认情况下,每个进程的当前工作目录是启动命令时所在目录,但跨节点时情况不同:每台目标节点上的工作目录不一定存在。

比如你在A机器上设定了工作目录C:\data\calc,目标节点B上如果不存在这个目录,进程就会启动失败或初始化为错误的当前目录。稳妥做法是显式用-wdir指定一个在所有节点上都存在的路径:

mpiexec -n 4 -hosts 2 win01 win02 -wdir C:\work calc.exe

如果你跑的程序需要从配置文件或数据文件读绝对路径,务必确认该路径在每台机器上都有对应文件。跨节点运行一个隐藏前提是:所有节点的文件系统对程序来说应该是相对一致的。注意MPI本身不提供共享文件系统的假设,如果你要多节点各读同一份数据,需要自己用网络共享盘或者把数据分发到每台机器上。

5.4 不同MPI实现的DLL混用问题

再强调一次DLL混用问题:一个程序用MS-MPI编译,运行的时候却加载了Open MPI的dll,这种绝对跑不起来。实际中出现这种混用,多半是系统PATH里有多个MPI的Bin目录,Windows的DLL搜索顺序导致错误dll被优先加载。

排查方法很简单:用进程监视工具看对应进程加载的mpi相关模块是不是msmpi.dll。如果看到一个进程加载了不同MPI实现的dll,比如msmpi.dll和libmpi.dll同时存在,几乎必然崩溃。排查思路是清理PATH,确保程序依赖的唯一MPI实现被优先加载。

还有一个版本兼容问题:MS-MPI 10.x的程序如果放到只装了8.x运行时的机器上,运行时会提示找不到msmpi.dll或报入口点错误。解决方式要么升级运行时到相同版本,要么用Dependencies确认程序依赖的具体版本再装对应运行时。

6. 验证环境与管理进程的心得

6.1 三步快速确认MPI环境可用

我每次在新机器上装好MPI,都会按固定三步验证,十几秒就能确认环境健壮性。

第一步,确认mpiexec可用:

mpiexec -help

能正常输出帮助信息,说明mpiexec本身和它的运行库都OK。

第二步,跑一个最小程序验证动态链接库可用。可以直接用MS-MPI自带示例,或者写一个上面那个hello程序编译成exe,跑-n 4,只要4个进程都能打印出rank,说明进程启动、PMI交互、标准输出都正常。

第三步,多节点验证。如果规划了跨节点运行,至少执行一次-hosts 2的方式,用-v输出确认所有节点都连接成功。这一步要覆盖smpd服务、防火墙、账号权限全链路。

6.2 常见退出码与报错的排查方向

MPI程序的错误信息虽然啰嗦,但指向性很强。我整理了平时最常看到的几种:

报错或现象常见原因优先排查方向
mpiexec不是内部或外部命令PATH未配置环境变量
unable to start smpd服务未启动、防火墙、杀毒拦截服务状态、防火墙规则、白名单
invalid mpi libraryDLL版本不匹配mpi.dll版本与mpiexec版本、PATH顺序
unable to allocate memory for pmi进程数太大或PMI空间不足降低-n数量、检查系统内存
processes not all reported in有节点宕机或网络故障ping目标机、检查smpd

还有些情况,退出码正常(0),但程序结果不对。最常见的是数据文件路径在每个节点不存在——进程全部正常启动、正常输出了错误结果,这种问题最坑。所以跨节点跑业务程序前,打印日志或中间校验值是个好习惯。

6.3 一些值得养成的操作习惯

最后分享几个我在实际项目里踩出来优化效率的习惯,不一定成体系,但每条都是真金白银换来的经验。

习惯一:所有MPI启动命令写进批处理脚本或PowerShell脚本,不要每次手敲。参数多了以后哪天改一个环境变量,手敲容易漏。脚本里统一在开头加上环境的set PATH操作,避免登录状态不同导致找不到命令。

习惯二:跨节点机器上,尽量保持相同的软件版本和相同目录结构。我在生产环境吃过亏:某节点只装了旧版运行库,导致整个集群任务失败率20%,查了一整天才定位到是一台机器的DLL版本不一致。

习惯三:跑大任务前,先用-n 2或-n 4小规模跑一遍,确认数据路径、环境变量、输出逻辑都正常,再放开到全规模。直接跑128进程出问题,日志量大到你根本不想排查。

习惯四:调试MPI问题时,主动打开诊断输出。MS-MPI可以通过环境变量开启更多诊断信息,Open MPI用--mca参数调子系统的日志级别。别指望默认输出就能告诉你一切,很多关键信息在诊断模式里才会打印。

对我来说,MPI的入门曲线其实不算陡,大部分挫败感都集中在mpiexec这一层。把它的工作逻辑和常见坑过一遍之后,后面写并行代码的过程反而顺心很多。希望这篇关于MPI和mpiexec.exe的经验整理,能帮你把环境搭建和调试的时间省下来,真正用在并行程序本身的优化上。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/7 16:46:53

Django后端开发实战:微信小程序档案管理服务搭建

做了两年多小程序&#xff0c;我越来越觉得&#xff1a;微信小程序这层壳其实不难&#xff0c;难的是背后那套能支撑业务的数据服务。这次分享的“档案宝”正是这样一个项目——前端是原生微信小程序&#xff0c;后端用Django搭了一套完整的档案管理服务&#xff0c;涵盖档案录…

作者头像 李华
网站建设 2026/10/7 16:46:09

MODIS地表温度数据处理全流程:QC解析、坐标配准与不确定性量化

简介&#xff1a;本资源为2022年中国全域1km分辨率地表温度&#xff08;LST&#xff09;空间分布数据集&#xff0c;基于NASA MODIS MOD11A2产品加工生成&#xff0c;面向遥感、地理信息、生态与气候研究领域的科研人员及GIS初学者&#xff0c;支撑区域热环境分析、城市热岛评估…

作者头像 李华
网站建设 2026/10/7 16:45:38

Linux线程详解:从pthread创建到同步互斥与死锁排查

刚接触Linux线程时&#xff0c;我犯过一个特别低级的错误&#xff1a;在线程入口函数里直接操作了一个全局变量&#xff0c;两个线程同时跑&#xff0c;结果那个计数器忽大忽小&#xff0c;跟抽风一样。后来慢慢啃完概念、踩过死锁的坑&#xff0c;才算摸清这套东西的脾气。今天…

作者头像 李华
网站建设 2026/10/7 16:45:38

江苏土壤类型标准Shapefile:可计算、可配准、可建模的GIS生产级数据

简介&#xff1a;本资源为江苏省土壤类型空间分布标准GIS数据集&#xff0c;面向地理信息、农业遥感、环境科学等领域的科研人员与高校师生&#xff0c;支撑区域土壤属性分析、生态评估及空间建模等基础研究工作。数据基于1∶400万中国土壤图构建&#xff0c;采用三位数字编码体…

作者头像 李华
网站建设 2026/10/7 16:45:17

微服务架构稳定性实践:服务保护与分布式事务方案对比与选型

做微服务这几年&#xff0c;我收到最多的技术问题其实翻来覆去就两类&#xff1a;线上服务无缘无故被打垮&#xff0c;然后数据账目对不上。前者是 服务保护 没做好&#xff0c;后者是 分布式事务 没捋清。尤其当你把单体应用拆成十几个微服务之后&#xff0c;这两个问题会…

作者头像 李华