Linux进程前后台管理jobs、fg、bg、nohup、disown你有没有过这种经历在终端里跑一个耗时脚本手一抖CtrlC把它掐了或者 SSH 连上去启动服务关掉窗口进程就没了。问题的根源在于——终端和进程之间有一条「会话」的脐带一旦终端断开内核会向它启动的进程发信号。这一篇把、CtrlZ、jobs、fg、bg、nohup、disown讲清楚掌握后就能自如地让进程在后台安稳运行。一、前台与后台终端只能伺候一个前台Linux 里一个终端terminal同一时刻只有一个前台进程组键盘输入、CtrlCSIGINT、CtrlZSIGTSTP都发给这个前台进程组。其他进程要么在后台跑要么被暂停。前台占着终端能接收键盘输入和CtrlC后台在跑但不占终端键盘信号发给不到它暂停被 SIGSTOP/SIGTSTP 冻结不占 CPU等fg或bg唤醒理解这条主线后面所有命令都顺了。二、启动即入后台语法格式命令 在整条命令末尾加一个shell 会把它放进后台立即把提示符还给你。示例与预期输出sleep 300 预期输出[1] 12345[1]是作业号job number是 shell 给这个任务编的号只在当前 shell 有效12345是进程号PID是内核编的全局唯一两者区别要记牢作业号管前后台切换PID 管杀进程。注意不等于「断开也不死」command 只是放后台它仍然属于当前终端会话。关掉终端它照样死。想断开也不死得配合nohup或disown见后文。三、CtrlZ把前台进程按暂停正在跑的进程想临时腾出终端按CtrlZ它会被 SIGTSTP 暂停并扔到后台状态T$ vim bigfile.log ^Z [1] Stopped vim bigfile.log注意是停止不是后台运行——它冻结了不消耗 CPU。想让它继续跑用bg想回到前台用fg。四、jobs查看当前 shell 的后台任务语法格式jobs [选项]常用参数参数说明-l同时显示 PID-r只显示运行中的任务-s只显示已停止的任务示例与预期输出jobs -l预期输出[1]- 12345 Running sleep 300 [2] 12346 Stopped vim bigfile.log符号解释当前任务fg不带参数时默认操作它-前一个任务Running/Stopped运行中 / 已暂停五、fg / bg前后台切换语法格式fg [%作业号] # 把后台任务调回前台 bg [%作业号] # 让暂停的任务在后台继续跑作业号要带%如%1不带参数则操作当前任务。示例与预期输出把[2]的 vim 唤回前台fg %2让刚才暂停的 vim 转到后台继续这个例子意义不大但换成rsync就很有用bg %1预期输出[1] sleep 300 ⚠️ 后台任务仍会往终端写输出后台进程的 stdout/stderr 默认还是连到终端它的输出会突然插进你正在敲的命令里把屏幕弄乱。启动时就重定向掉long_task.sh run.log 21 这也是写后台任务脚本的标配姿势。六、nohup挂断也不死nohup no hang up。它让命令忽略 SIGHUP 信号并把输出默认重定向到nohup.out。语法格式nohup 命令 [参数] 示例与预期输出nohup python3 train.py train.log 21 预期输出[1] 12345 nohup: ignoring input and appending output to nohup.out这里因为已经手动重定向到train.log所以不会再生成nohup.out。关键点nohup只解决 SIGHUP不解决 stdin。它在启动时会把 stdin 重定向到 /dev/null所以别再指望进程能读键盘。通常写作nohup cmd out.log 21 忽略挂断 标准输出 错误输出 后台一步到位。想停止它用ps/pgrep找到 PID 再kill因为作业号在关掉 shell 后就没了。⚠️ nohup 不等于守护进程nohup进程仍然是普通进程跟着系统走但不会开机自启、不会自动重启、不受服务管理。生产环境的长期服务应该交给systemd见第 35 篇nohup只适合临时跑个长任务。七、disown事后补救有时进程已经在跑了可能还带你才想起来「糟了关终端就没了」。这时不用重开disown可以把作业从 shell 的作业表里摘掉让它不再收到 SIGHUP。语法格式disown [选项] [%作业号]常用参数参数说明-h只标记为「不接收 SIGHUP」任务仍留在 jobs 列表-a移除所有作业-r只移除运行中的作业示例与预期输出sleep 300 disown %1 jobsjobs会显示[1]已不在列表里但sleep进程还在pgrep -a sleep 12345 sleep 300nohup vs disown 怎么选维度nohupdisown使用时机启动时就加跑起来后补输出默认写 nohup.out不变仍指向原位置灵活性低写死在命令前高可对已有任务操作八、⚠️ 常见错误以为就能防断开。只是后台SSH 一断照样收到 SIGHUP 退出。要断开也不死必须nohup或disown或用screen/tmux。用CtrlZ后忘了恢复。很多人CtrlZ停了 vim 又去干别的回来发现 jobs 里堆了一串 Stopped占用着资源。记得fg或kill %n清理。nohup没重定向输出导致磁盘写满。默认写nohup.out且不断追加长时间跑的任务如果没重定向某天会把/撑爆。永远显式 xxx.log 21。作业号跨 shell 失效。jobs的作业号只在当前 shell 里认新开一个终端fg %1会报no such job。跨会话管理进程要靠 PID 或tmux。kill %1与kill 1搞混。%1是作业号1是 PID 1initkill 1可能把系统搞挂虽然一般会权限拒绝。杀后台任务时写清%。九、原理探究SIGHUP 到底是什么SIGHUP 字面是「挂断」Hang Up起源于拨号上网时代——调制解调器掉线内核就向该终端上的所有进程发 SIGHUP。如今终端虽不用电话线但这个信号的语义保留了下来当控制终端controlling terminal关闭时内核会向前台进程组和该会话的所有进程发送 SIGHUP。一条 SSH 连接对应一个会话session会话里有一个控制终端。你关掉 SSH 窗口控制终端消失内核给会话里的进程发 SIGHUP默认动作是终止进程。这就解释了三件事nohup为什么有效它启动进程时就把 SIGHUP忽略signal(SIGHUP, SIG_IGN)收到也不理。disown为什么有效修订进程的作业表让 shell 在退出时不向它转发 SIGHUP。为什么有的进程「关终端也没事」它可能自己就忽略了 SIGHUP或者已经脱离了这个会话如systemd启动的服务。顺带理解「进程组 / 会话」一个会话包含多个进程组一个进程组包含多个进程。CtrlC发给前台进程组全体所以a | b | c管道里三个进程会一起收到 SIGINT。想只杀管道里最后一个得用kill精确到 PID。十、更靠谱的方案tmux 与 systemdnohup/disown是轻量解决方案但有两个硬伤进程输出只能靠日志捞、进程管理混乱一多就靠ps找 PID。实际工作中更推荐# 方案一tmux会话可随时 attach/detach tmux new -s deploy # 在里面跑任务然后 CtrlB 再按 D 脱离重连 tmux attach -t deploy # 方案二写 systemd 服务长期服务交给系统管 sudo systemctl start myapp sudo systemctl enable myapp # 开机自启 journalctl -u myapp -f # 看日志临时长任务跑个数据、下个大文件tmux最优随时回来接着看。长期服务web、mq、agent写systemdunit开机自启、崩溃重启、日志集中见第 35、36 篇。nohup/disown理解原理是必须的但日常尽量用tmux和systemd替代。