Linux nohup 命令详解

前段时间逛论坛看到有人在研究 nohup 这个命令,突然意识到自己没有研究过这个常用命令,这次整理下。

nohup 属于 coreutils

这里先贴上部分核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* nohup -- run a command immune to hangups, with output to a non-tty
...
*/

int
main (int argc, char **argv)
{
// 处理 stdout 和 stderr 的重定向
// ......

signal (SIGHUP, SIG_IGN);

char **cmd = argv + optind;
execvp (*cmd, cmd);

// ......
// 处理 返回值
}

可以看到核心代码还是很简洁的,忽略 SIGHUP 信号,执行 nohup 之后指定的命令,命令的 stdoutstderr 输出一般会被重定向至当前目录的 nohup.out 文件中。

忽略 SIGHUP 信号

其中 signal 是处理信号的函数,函数原型如下:

1
2
3
typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

信号处理函数的参数可以是 SIG_IGN 忽略信号,SIG_DFL 使用默认的信号处理机制以及用户自定义的函数地址。

Termination Signals (The GNU C Library) 可以查看到 SIGHUP 的含义:

The SIGHUP (“hang-up”) signal is used to report that the user’s terminal is disconnected, perhaps because a network or telephone connection was broken. For more information about this, see Control Modes.

This signal is also used to report the termination of the controlling process on a terminal to jobs associated with that session; this termination effectively disconnects all processes in the session from the controlling terminal. For more information, see Termination Internals.

通常来说,终端断开时,所有通过该终端运行的进程会收到 SIGHUP 信号,对应的默认机制是进程退出。这里忽略了 SIGHUP 信号,所有使用 nohup 执行的命令,在终端断开后仍能继续在后台运行。

执行命令

其中 execve 用来执行程序,nohup 中使用的 execvp 在定位命令的行为上稍有不同。

The exec() family of functions replaces the current process image with a new process image.

exec() 这一系列的函数都会把当前进程替换为新的进程,其中有些东西是不会被继承的:

The dispositions of any signals that are being caught are reset to the default (signal(7)).

被捕捉并处理的信号会被重置,注意这里的 caught,指的是用户定义的信号处理函数,因为进程替换,用户定义的函数地址不再有效,所以对应的信号处理函数被重置为默认值。

但是 nohup 中的 SIG_IGN 是直接忽略,所以会保留至 exec 替换的新进程中。

忽略输入

nohup 会重定向 stdoutstderr,但不会影响 stdin

所以通常情况下,会使用 nohup command & 来将命令置入后台执行,这样就不会占用终端的输入,用户可以继续执行其他命令。