nohup

Posted on | 2023 words | ~5 mins
Linux

nohup是一个POSIX命令。人如其名“NO”+“HUP(hangup)”,“HUP”是挂起信号,“NOHUP”就是忽略挂起信号。

实用版说明

NOHUP

1$ nohup ping www.baidu.com > log.txt

执行该命令后,当前ping命令会保持在前台运行,即我们没法继续在当前terminal执行其它命令。如果我们开启一个新的terminal,然后执行:

1$ wc -l log.txt
224 log.txt
3
4$ wc -l log.txt
528 log.txt

会看到wc输出的数字在持续增长中。现在关闭运行nohup命令的terminal(即发出了SIGHUP信号)。在第二个terminal(运行wc命令的那个),再次执行:

1$ wc -l log.txt
232 log.txt
3
4$ wc -l log.txt
535 log.txt

就能看到log.txt文件的行数依旧在持续增长,这说明ping命令并没有因为SIGHUP信号而终止。

关于&符号

1$ nohup ping www.baidu.com > log.txt &

执行该命令后,ping会在后台运行(即,我们可以在当前terminal内执行其他命令)。如果我们查看log.txt文件,就会看到文件在持续变大。nohup的组合确保了命令可以永久在后台执行。

关于重定向操作符

1$ nohup ping www.baidu.com > log.txt 2>&1 &
  • >操作符右侧接一个文件。如果文件不存在,则创建新文件,并将左侧命令输出覆盖到文件中
  • >>操作符右侧同样接一个文件。如果文件不存在,则创建新文件,并将左侧命令输出追加到文件中
  • 0是标准输入(stdin),1是标准输出(stdout),2是标准错误输出(stderr)。2>&1是将标准错误重定向到标准输出。最后**> log.txt**再将标准输出重定向到文件log.txt

理论版说明

为了讲清楚NOHUP的机制,必须要先介绍一些概念:

进程组

一个进程及其子进程形成一个进程组进程组中的第一个进程,称为进程组领导者(process group leader)。每个进程有一个ID(PID)。进程的父进程的ID叫PPID。进程组ID(PGID)用进程组领导者的PID。举个例子:

1$ nohup ping www.baidu.com | less

会产生一个进程组进程组会包含至少三个进程nohup``ping``less

会话

一个会话会由多个进程组组成。创建会话的进程称为会话领导者(session leader)。每个会话有一个ID(SID),为会话领导者进程ID。

终端(terminal)

过去终端是一个真实的物理设备,支持输入(键盘)和显示输出(显示器),用来操作主机。现在当我们提到终端,是一个抽象的概念,通常是指操作系统的一个应用。我们通过终端应用向会话输入信号,并将会话的内容显示出来。

Shell

当我们在Linux系统中,启动一个终端,实际上启动了一个进程,该进程执行shell应用,解释和处理各种命令。这个进程作为会话领导者形成了一个新会话。当然隐含着也创建了一个进程组。如果我们打开两个终端,那就开启了两个会话。举个例子:

先启动一个终端,执行

1$ nohup www.bing.com &
2
3$ ping www.baidu.com

再启动一个终端,执行

1$ nohup www.google.com &

然后让我们用ps j看看这几个进程和会话:

 1===== ===== ===== ===== ===== ====================
 2PPID  PID   PGID  SID   TPGID COMMAND
 3===== ===== ===== ===== ===== ====================
 4 6133 12708  5693  5693 5693  SecureCRT
 512708 12720 12720 12720 18176 -zsh
 612708 12786 12786 12786 18191 -zsh
 712720 18165 18165 12720 18176 ping www.bing.com
 812720 18176 18176 12720 18176 ping www.baidu.com
 912786 18184 18184 12786 18191 ping www.google.com
1012786 18191 18191 12786 18191 ps j
11===== ===== ===== ===== ===== ====================

可看到几个事情:

  • www.bing.comwww.baidu.com的SID是一样的,在同一个会话中
  • www.google.com则在另一个会话中
  • www.bing.comwww.baidu.com是两个命令,故在两个不同的进程组,PGID不同;两个命令的PPID相同,同属于zsh创建的子进程组 (本示例中,我的shell是zsh)
  • www.google.com的PPID也指向一个zsh。而这两个zsh最终都指向SecureCRT所在进程。

此外每个会话会对应一个前台进程组(TPGID, tty process group id),其他进程组都属于后台进程组。 会话中只有前台进程组可以接收终端的输入和信号。 启动时,会话领导者终端控制进程(通常就是Shell进程)。随着控制终端的进程变化,终端会修改前台进程组。从上面的表格即可看出,终端中当前正在执行的进程所在进程组被设置为TPG。控制进程组永远是会话领导者所在组;前台进程组则根据当前哪个进程正在执行来变化。

  • 第二行的-zshping www.bing.comping www.baidu.com的TPGID均为正在终端前台运行的ping www.baidu.com所在进程ID。
  • 第三行的-zsh,ping www.google.comps j的TPGID则是正在运行的ps j所在进程ID

SIGHUP

当关闭终端时,系统发送SIGHUP信号给控制进程。当控制进程退出时,会将SIGHUP发给所在进程组中每一个进程。然后向前台进程组发信号。如果不希望自己的进程退出,就应该使用nohup&组合将进程放到后台且忽略SIGHUP信号。

关闭终端 后,使用ps j会发现本应继续运行的进程不见了。难道是被杀掉了?不是说好的nohup吗?请用ps -aux试一下,是不是又找到那些后台进程了?x确保ps命令显示所有进程,而不是按终端来显示。