srun --kill-on-bad-exit遇致命错误未终止进程的原因及解决方法
问题详情
使用命令srun --kill-on-bad-exit python -O my_script.py运行多GPU PyTorch训练时,因磁盘已满触发一系列致命错误,但srun未按预期终止所有进程,反而变为僵尸进程直至任务超时。相关错误顺序如下:
[Rank 3] Watchdog caught collective operation timeout: WorkNCCL(SeqNum=34130, OpType=ALLGATHER, NumelIn=1, NumelOut=4, Timeout(ms)=1800000) ran for 1800093 milliseconds before timing out.terminate called after throwing an instance of 'c10::DistBackendError'srun: error: gh054: task 0: Abortedslurmstepd: error: *** STEP 848926.2 ON gh054 CANCELLED AT 2025-07-22T17:36:08 ***
疑问:为何--kill-on-bad-exit未生效?若不是Slurm Bug,如何强制srun在遇此类致命错误时终止整个任务?
原因解析
--kill-on-bad-exit的核心逻辑是:当某个任务以非零退出码正常终止时,自动终止其余所有任务。但你的场景中存在两个关键问题导致该参数失效:
- 进程终止方式异常:NCCL超时触发的是C++异常抛出,最终通过
SIGABRT信号强制终止进程,而非进程主动返回非零退出码。这种信号终止的场景下,srun可能无法及时捕获并触发全局终止逻辑。 - 磁盘满引发的僵死状态:磁盘已满会导致进程在终止时无法完成日志写入、资源清理等操作,进程陷入僵死,无法向
srun传递明确的终止信号,使得srun无法判定任务已异常,进而不执行杀死其余进程的操作。
解决方法
1. 调整srun参数组合
使用--wait=0配合--kill-on-bad-exit,让srun在第一个任务终止后立刻退出并杀死所有剩余任务,无需等待所有进程结束:
srun --kill-on-bad-exit --wait=0 python -O my_script.py
2. 强制超时信号终止
通过--signal参数指定,若任务在指定时间内未正常结束,直接发送SIGKILL强制杀死所有进程(避免僵死):
srun --kill-on-bad-exit --signal=SIGKILL@60 python -O my_script.py
这里的@60表示等待60秒后发送信号,可根据实际需求调整时长。
3. 在训练代码中主动触发终止
在PyTorch代码中添加异常捕获逻辑,一旦检测到磁盘满或NCCL通信异常,主动触发全局终止:
import os import torch import torch.distributed as dist try: # 你的训练逻辑 ... except (IOError, RuntimeError) as e: # 捕获磁盘满或NCCL相关异常 if "disk full" in str(e).lower() or "NCCL" in str(e): # 销毁进程组 if dist.is_initialized(): dist.destroy_process_group() # 强制以非零码退出,触发srun的--kill-on-bad-exit os._exit(1)
4. 启用Slurm资源绑定强制检查
添加--gres-flags=enforce-binding确保GPU与进程正确绑定,减少NCCL通信超时的概率(辅助优化,从根源减少异常触发):
srun --kill-on-bad-exit --gres-flags=enforce-binding python -O my_script.py
内容的提问来源于stack exchange,提问作者profPlum
相关产品推荐
相关产品推荐

