使用Ruby创建子进程后无法关闭SSH连接的原因及解决办法
SSH关闭时Ruby脚本挂起的原因与解决办法
问题原因
当通过SSH运行Ruby脚本后关闭连接,SSH会话的控制终端会向前台进程组的所有进程发送SIGHUP信号。父进程收到信号后会退出,但fork出的子进程即便用Process.detach处理(避免僵尸进程),仍持有原终端的标准输入、输出、错误文件描述符。此时SSH会话因终端资源被子进程占用,无法正常终止,导致连接挂起,需手动输入~ + RETURN强制退出。
解决办法
1. 让子进程彻底脱离终端(创建守护进程)
在子进程中执行会话分离与IO重定向,使其完全脱离原终端,父进程仍可正常输出:
pid = fork do # 成为新会话的领导者,脱离原终端控制 Process.setsid # 关闭并重定向子进程的标准IO STDIN.reopen('/dev/null') STDOUT.reopen('/dev/null', 'a') STDERR.reopen('/dev/null', 'a') loop do sleep 1 end end Process.detach(pid)
2. 仅重定向子进程的IO,保留父进程输出
若无需子进程输出,仅在子进程内部重定向其标准IO,父进程的输出不受影响:
pid = fork do # 子进程单独重定向标准IO到/dev/null STDIN.reopen('/dev/null') STDOUT.reopen('/dev/null', 'w') STDERR.reopen('/dev/null', 'w') loop do sleep 1 end end Process.detach(pid) # 父进程可正常输出到终端 puts "父进程的输出会保留在SSH会话中"
3. 捕获SIGHUP信号,主动终止子进程
如果希望子进程随SSH连接关闭而终止,可在父进程中捕获SIGHUP信号,主动杀掉子进程:
pid = fork do loop do sleep 1 end end Process.detach(pid) # 捕获SIGHUP信号,终止子进程后退出父进程 trap('SIGHUP') do Process.kill('TERM', pid) exit end # 保持父进程运行(按需调整) sleep
内容的提问来源于stack exchange,提问作者sudoremo
相关产品推荐
相关产品推荐

