BitaHub开发机/开发环境命令中断问题
在 BitaHub 开发机或开发环境中运行深度学习训练、数据预处理或长周期脚本时,经常遇到命令意外中断退出的情况(如断网、合盖休眠或长时间无操作断开)。针对这一问题,本文整理了问题产生原因以及多种实用的解决方案。
一、问题现象与日志特征
当用户在终端(本地 SSH 客户端、网页端 Web Terminal 或 JupyterLab Terminal)中直接在前台执行任务时:
python -m torch.distributed.run --nproc_per_node=4 train.py
若遇到本地网络抖动断开、电脑休眠锁屏、关闭浏览器终端标签页,或者 SSH 会话因空闲超时断开,训练任务会立即意外终止。
在训练日志中通常会捕获到类似如下的退出日志:
[rank0]: Traceback (most recent call last):
...
ERROR:torch.distributed.elastic.multiprocessing.api:failed (exitcode: -1) ...
INFO:torch.distributed.elastic.agent.server.api:Received signal: 1 (SIGHUP)
INFO:torch.distributed.elastic.agent.server.api:Sending SIGTERM to all child workers...
二、原因分析
该现象源于 Linux 操作系统的终端会话控制与挂断机制:
- 终端绑定关系(TTY/PTS Binding):
在终端前台直接运行命令启动程序时,该进程作为前台进程组直接绑定在当前打开的虚拟终端伪设备(
pts/x)上。 - SIGHUP(信号 1)触发逻辑:
当发生以下任一情况时,控制终端的连接即告断开:
- 本地网络出现轻微丢包或切换 Wi-Fi;
- 个人电脑休眠、合盖或断网锁屏;
- 关闭浏览器中的 Web Terminal 或 JupyterLab Terminal 页面;
- SSH 客户端因长时间无交互达到超时阈值断开连接。
- 级联终止传播:
终端连接断开后,Linux 内核会向该终端会话控制下的所有前台进程广播发送
SIGHUP(Signal 1,Hangup 挂断信号)。 - 框架安全退出响应:
以
torchrun(PyTorch Elastic Agent)为例,其主进程捕获到signal: 1 (SIGHUP)后,判定父终端已挂断,出于释放 GPU 资源与显存的安全机制,会主动向所有 Worker 进程分发SIGTERM信号并退出。
三、解决方案
长周期任务建议脱离前台控制终端运行,或为 SSH 连接配置心跳保活。以下方案可根据使用场景选择:
方案一:使用 tmux 终端复用器(强烈推荐)
tmux 能够创建与当前网络连接解耦的独立虚拟会话。即使本地断网、重启电脑或关闭浏览器,会话进程依然会在开发机后台持续运行。
1. 创建并进入专属训练会话
tmux new -s train
2. 在 tmux 窗口中启动训练任务
python -m torch.distributed.run --nproc_per_node=4 train.py
3. 分离挂起会话
在任务正常运行后:
- 键盘按下快捷键组合:Ctrl + B,松开后再按下 D(Detach);
- 终端将返回原始界面,并提示
[detached (from session train)]; - 此时即可安全关闭终端、关闭浏览器、电脑休眠或关机,后台训练不受影响。
4. 重新接入查看训练进度
再次连上开发机后,执行以下命令即可恢复当时的实时训练控制台:
tmux attach -t train
- 查看当前所有会话:
tmux ls - 任务结束后退出并销毁会话:在 tmux 内部输入
exit或执行tmux kill-session -t train
方案二:配置 SSH 客户端心跳保活
如果习惯在终端前台运行命令,但经常因几分钟未操作导致 SSH 连接被路由器/网关切断,可以在本地 SSH 客户端中开启自动心跳发送。
方式 A:全局配置文件持久化(推荐)
在本地电脑的用户主目录下编辑 ~/.ssh/config(Windows 为 C:\Users\<用户名>\.ssh\config),追加以下保活参数:
Host *
ServerAliveInterval 30
ServerAliveCountMax 5
TCPKeepAlive yes
ServerAliveInterval 30:客户端每隔 30 秒自动向开发机发送一次心跳空包,告知中间网络设备连接依然活跃;ServerAliveCountMax 5:若连续 5 次发送心跳均未收到响应,才判定连接彻底断开;- 配置后所有新建的 SSH 会话均会自动生效,有效解决空闲断连问题。
方式 B:单次命令行直接附带心跳参数
通过 SSH 连接时直接在命令中注入保活参数:
ssh -o ServerAliveInterval=30 -o ServerAliveCountMax=5 -p <端口> root@<开发机IP>
心跳保活主要解决空闲网络断开问题。若电脑休眠、合盖或断网,SSH 连接仍会物理中断并触发 SIGHUP。对于数小时以上的长时间训练,仍需配合方案一(tmux)或方案三(nohup)使用。
方案三:使用 nohup 结合重定向脱壳运行
无需交互操作的任务,可以使用 Linux 原生自带的 nohup(No Hangup)命令忽略 SIGHUP 信号,并将输出重定向至日志文件:
1. 后台非阻塞启动训练
nohup python -m torch.distributed.run --nproc_per_node=4 train.py > train.log 2>&1 &
nohup:使操作系统在终端断开并发送SIGHUP信号时予以忽略,不终止进程;> train.log:将程序标准输出重定向到train.log日志文件中;2>&1:将标准错误(stderr)合并输出到标准输出(stdout),防止报错信息丢失;&:将任务置入后台异步执行,立即释放当前终端提示符。
2. 追踪查看训练日志
执行上述命令后,终端会打印生成的后台进程 PID。可以通过 tail 命令实时动态追踪控制台输出:
tail -f train.log
按 Ctrl + C 退出日志跟踪,此操作不会影响后台实际正在运行的训练进程。
四、排查与进程治理速查
若需要确认任务是否仍在后台运行,可按以下步骤排查与管理:
- 查看 GPU 显卡占用:
nvidia-smi
若下方 Processes 栏依然有 python 进程且显存被占用,说明训练仍在正常计算中。
- 检索训练进程 ID:
ps aux | grep train.py
- 终止失控或卡死的后台任务:
kill -9 <进程PID>