Linux后台任务管理:jobs、nohup、disown与& (2024)

0. 前言

最近在课题组的 Linux 集群上跑程序,希望程序转到后台运行,避免他人因操作失误使我的程序被关闭,查阅相关资料后发现我的需求是 Linux 任务管理的一部分。本文记录总结相关信息,以供后续查阅使用。

主要用到的 Linux 命令( command ):jobsfgbgkillpsnohupdisown&

上述命令可在网上查阅其使用细节,也可以在 Shell 中输入 command --help 或 man command 查阅其信息。

本文使用名为 running.sh 的 shellscript 作为演示程序,其功能为每隔1秒输出一次当前日期与时间:

$ cat running.sh#!/bin/bashwhile true;do date +"PID[$$] : the current date and time is %F %T" sleep 1sdone

其中,方括号内的 $$ 表示获取当前 shellscript 运行的进程ID。

1. 工作管理指令 jobs

尝试运行一下 running.sh

$ bash running.shPID[2168] : the current date and time is 2021-12-18 10:19:41PID[2168] : the current date and time is 2021-12-18 10:19:42PID[2168] : the current date and time is 2021-12-18 10:19:43

想要终止程序运行,只需按下键盘上的 CTRLC 即可,也可以通过 CTRLZ 让程序暂停运行。

尝试一下 CTRLZ

PID[2168] : the current date and time is 2021-12-18 10:19:45PID[2168] : the current date and time is 2021-12-18 10:19:46PID[2168] : the current date and time is 2021-12-18 10:19:47^Z[1]+ Stopped bash running.sh$

这时屏幕上停止了输出,并且处于交互状态,可以自由输入命令,尝试输入 jobs -l 命令:

$ jobs -l[1]+ 2168 Stopped bash running.sh

其中,方括号内的数字1表示工作ID,数字2168为进程号( PID ),Stopped 为工作状态,bash running.sh 为工作名(进程名)。

这时,输入命令 fg %1 就会让工作ID为1的程序,即 bash running.sh ,重新回到前台运行,输入命令 bg %1 会让程序进入后台运行。

尝试一下 bg %1 命令,让程序进入后台运行:

$ bg %1[1]+ bash running.sh &PID[2168] : the current date and time is 2021-12-18 10:19:55PID[2168] : the current date and time is 2021-12-18 10:19:56PID[2168] : the current date and time is 2021-12-18 10:19:57

这时会先输出一句 ' [1]+ bash running.sh & ',其中' & '表示工作ID为1的 bash running.sh 在后台运行。

此时屏幕上依然会有输出,这是因为程序虽然转入了后台运行,但是其输出还是当前屏幕,不仅如此,我们会发现按下 CRTL+C 或者 CRTL+Z 都不起作用,这是因为此时我们的终端(屏幕)处于交互状态( interactive ),后台程序会忽略 CRTL+CCRTL+Z 发出的信号( SIGNAL )。Linux 的 General Commands Manual 中关于 SIGNALS 描述如下:

SIGNALS When bash is interactive, in the absence of any traps, it ignores SIGTERM (so that kill 0 does not kill an in‐ teractive shell), and SIGINT is caught and handled (so that the wait builtin is interruptible). In all cases, bash ignores SIGQUIT. If job control is in effect, bash ignores SIGTTIN, SIGTTOU, and SIGTSTP. Non-builtin commands run by bash have signal handlers set to the values inherited by the shell from its parent. When job control is not in effect, asynchronous commands ignore SIGINT and SIGQUIT in addition to these inher‐ ited handlers. Commands run as a result of command substitution ignore the keyboard-generated job control sig‐ nals SIGTTIN, SIGTTOU, and SIGTSTP. The shell exits by default upon receipt of a SIGHUP. Before exiting, an interactive shell resends the SIGHUP to all jobs, running or stopped. Stopped jobs are sent SIGCONT to ensure that they receive the SIGHUP. To prevent the shell from sending the signal to a particular job, it should be removed from the jobs table with the disown builtin (see SHELL BUILTIN COMMANDS below) or marked to not receive SIGHUP using disown -h. If the huponexit shell option has been set with shopt, bash sends a SIGHUP to all jobs when an interactive lo‐ gin shell exits. If bash is waiting for a command to complete and receives a signal for which a trap has been set, the trap will not be executed until the command completes. When bash is waiting for an asynchronous command via the wait builtin, the reception of a signal for which a trap has been set will cause the wait builtin to return immedi‐ ately with an exit status greater than 128, immediately after which the trap is executed.

这说明虽然屏幕上一直有输出,但是屏幕(终端)还是可以输入命令的。只要利用 fg 命令就可以让工作再次进入前台,这次输入命令的过程会非常难受,因为输入命令字符的时候,还有程序不断的在屏幕上输出字符,但只要正确输入,就不会有问题 :

$ fg %1bash running.shPID[2168] : the current date and time is 2021-12-18 10:20:16PID[2168] : the current date and time is 2021-12-18 10:20:17PID[2168] : the current date and time is 2021-12-18 10:20:18

与使用 bg 命令时相似,先输出一句 ' bash running.sh ',表明程序已经来到前台运行。这时 CTRL+CCTRL+Z 都会起作用了。

小结:通过 jobs 命令查看工作ID,使用 bg 命令可以把前台程序放入后台,使用 fg 命令可以把后台程序拉到前台,jobsbgfg 的配合使用能有效管理前后台任务。

2. 输出重定向

在上一节中,即使 running.sh 放在后台运行,其输出也是屏幕(终端),虽然这方便我们观察程序输出,但另一方面也防碍了我们继续利用当前屏幕进行工作。为了不让程序输出到屏幕,需要用到重定向命令:

命令说明
command-line > file将标准输出( stdout )写入文件file
command-line >> file同上,但是以追加的方式写入
command-line n> file将文件描述符为n的文件写入文件file
command-line n>> file同上,但是以追加的方式写入
command-line n> file m>&n将文件描述符m定向到n,然后一起写入文件file
command-line n>> file m>&n同上,但是以追加的方式写入

*文件描述符 :通常 0 是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)

更加详细的内容可以查看:

一般程序运行时,系统给与当前程序的标准输入、标准输出、标准错误输出都是屏幕(终端),通过重定向命令可以改变输出位置,写到其他文件里面去:

# 将标准输出1定向到 file.out 标准错误输出2定向到 file.err$ bash running.sh 1> file.out 2> file.err# 或者将1与2合并定向到 file.all$ bash running.sh 1> file.all 2>&1# 只把1定向到 file.out 2还是保持输出到屏幕$ bash running.sh 1> file.out# 如果 > 符号前没有加描述符,默认为标准输出1$ bash running.sh > file.out# 重定向到 /dev/null , /dev/null 就像一个无底洞,可以无限往里面扔东西,坏处就是程序输出了啥咱也不知道$ bash running.sh > /dev/null 2>&1

3. 后台运行命令 &

在第一节中通过 CTRL+Z 暂停任务,配合 bg 命令可以将任务放入后台。如果想程序一开始就进入后台运行,可以通过 & 命令:

# 直接进入后台运行 command-line &$ bash running.sh > file.out &[1] 2529# jobs 查看任务状态$ jobs -l[1]+ 2529 Running bash running.sh > file.out &# tail 查看file.out$ tail -f file.outPID[2529] : the current date and time is 2021-12-18 13:39:45PID[2529] : the current date and time is 2021-12-18 13:39:46

可以看到,使用 & 命令后,程序直接进入后台,并且在屏幕上输出其工作ID与进程ID。

4. 能结束程序运行的命令 kill

在第一节中通过 fg 命令可以将后台任务调回前台,然后用 CTRL+C 的方式结束程序运行。如果想直接结束后台任务,就需要用到 kill 命令,其实际功能是向某一任务发送信号,结束程序的信号只是众多信号中的一部分:

# kill的描述: 向一个 job 发送一个 signal $ kill --helpkill: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec] Send a signal to a job.# signal 列表$ kill -L 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR111) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+338) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+843) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+1348) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-1253) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-758) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-263) SIGRTMAX-1 64) SIGRTMAX

上文中提到的 CTRL+C 其实就是向前台程序发出 2) SIGINT 信号, CTRL+Z 发出的是 20) SIGTSTP 信号;

如果不指定 kill 发出的信号,则默认为 15) SIGTERM,终止任务;

用的最多的就是 9) SIGKILL 信号,无条件结束任务。

# 后台运行任务$ bash running.sh > file.out 2>&1 &[1] 2762# kill 结束任务$ kill %1# or kill 2762 or kill -2 %1 or kill -2 2762 or kill -9 %1 or kill -9 2762

5. 让程序无视 SIGHUP 信号的命令 nohup 与 disown

第一节与第四节中分别提到了 Linux 中的各类信号,其中 SIGHUP 信号是比较特殊的一种,它会在 Shell 断开时,即退出终端或者断线的情况下,自动向该 Shell 中的子程序发送 SIGHUP 信号,让子程序全都停止运行。

为了确保程序即使在终端断开链接后仍然可以在后台运行,需要用到 nohup 或者 disown 命令:

# 常见的 nohup 命令$ nohup bash running.sh > file.all 2>&1 &# 或者使用 disown -h 命令$ bash running.sh > file.all 2>&1 & disown -h# disown 命令可以在任务中途使用,假定此时后台有一任务,工作号为1$ disown -h %1

这两个命令的效果是一致的,都是通过将程序挂载到另外的进程下(最典型的就是1号系统进程),使得程序不受当前终端的 SIGHUP 信号影响。表现出来的结果就是当断线重连后,利用 pstree -p 命令显示进程树,会发现程序已经移动到别的进程下了:

# 当前 bash shell 进程号是 3616 , running.sh 程序进程号是 3504$ pstree -p 3504init(1)─┬─init(3403)───bash(3504)───sleep(3644) ├─init(3614)───init(3615)───bash(3616)───pstree(3645)

这时在 jobs 列表中无法看到 running.sh 程序,只能通过 ps -aux 命令或者查看输出文件里面的 PID 来找到 running.sh 所在进程。

此时关闭程序也只能使用 kill 命令:

# kill -9 无条件关闭任务$ kill -9 3504

6. 总结

命令用法说明
jobsjobs -l查看任务详情
bgbg %工作ID将工作ID任务调入后台运行
fgfg %工作ID将工作ID任务调入前台运行
killkill -n [ PID | %工作ID ]向任务发送信号
&command-line &将任务立即放入后台运行
nohupnohup command-line忽视 SIGHUP 信号
disowncommand-line disown -h
or
disown -h %工作ID
忽视 SIGHUP 信号

参考资料:

https://www.baeldung.com/linux/job-control-disown-nohup

https://linuxize.com/post/how-to-run-linux-commands-in-background/

https://zhuanlan.zhihu.com/p/96909198

Linux后台任务管理:jobs、nohup、disown与& (2024)

References

Top Articles
Latest Posts
Article information

Author: Van Hayes

Last Updated:

Views: 6132

Rating: 4.6 / 5 (66 voted)

Reviews: 89% of readers found this page helpful

Author information

Name: Van Hayes

Birthday: 1994-06-07

Address: 2004 Kling Rapid, New Destiny, MT 64658-2367

Phone: +512425013758

Job: National Farming Director

Hobby: Reading, Polo, Genealogy, amateur radio, Scouting, Stand-up comedy, Cryptography

Introduction: My name is Van Hayes, I am a thankful, friendly, smiling, calm, powerful, fine, enthusiastic person who loves writing and wants to share my knowledge and understanding with you.