目录

nohup详解

nohup介绍

nohup 是 no hang up 的缩写,就是不挂断的意思。

  • 用途:不挂断地运行命令
  • 语法:nohup Command [ Arg … ] [ & ]
  • 描述:nohup 命令运行由 Command 参数和任何相关的 Arg 参数指定的命令,忽略所有挂断(SIGHUP)信号。在注销后使用 nohup 命令运行后台中的程序。要运行后台中的 nohup 命令,添加 & ( 表示“and”的符号)到命令的尾部。

nohup与&的区别

开始之前先介绍linux的两个signal信号

  • sigup (hang up) 挂断信号,eg 关闭终端
  • sigint (interrupt) 中断信号,eg 交互程序执行ctrl+c

演示

编写个c文件,程序很简单,每隔1s输出一句hello

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#include <stdio.h>
#include <unistd.h>

int main() {
    int i = 0;
    while(1) {
        printf("hello,%d\n", i++);
        sleep(1);
    }
}

编译成文件名为a,运行$ gcc -o a a.c,执行./a

http://img.cana.space/picStore/20201124190600.png

使用 ./a& 后台运行程序,会是什么效果呢?

http://img.cana.space/picStore/20201124191412.png

如上图,首先会在终端显示进程号45002,键入ctrl+c,发出sigint信号,程序会继续运行

此时如果关掉session,程序会收到一个SIGHUP信号,此时会怎么样呢?

1
ps -ef | grep -w a

http://img.cana.space/picStore/20201124191654.png

可以看到关闭session之后,进程号是45002的a进程也关闭了。

使用nohup ./a.out 又会是什么效果呢?

http://img.cana.space/picStore/20201124191804.png

使用nohup 运行程序a.out,会发现:有一个提示,hello的输出也没有出现在前台

http://img.cana.space/picStore/20201124191900.png

手动ps看进程号,这次a的进程号是45160。

此时如果关掉session,程序会收到一个SIGHUP信号,程序会不会关闭呢?

关掉session后,再次ps看一下,ID为32437的a.out进程还在。

这时候只能通过kill把程序干掉了,killall之后,ps查看进程已经关闭。

http://img.cana.space/picStore/20201124192052.png

killall
killall:杀死所有同名进程

killall之后,查看发现多了一个nohup.out文件,不过这个文件的大小是0,有点奇怪,启动程序的时候,明明提示了“appending output to nohup.out”呀,先把问题遗留在这,测试一下Ctrl +C。

使用nohup启动a.out,如果键入Ctrl+C ,程序收到SIGINT信号后,直接关闭了。

使用nohup ./a & 运行程序后,可以看到:

http://img.cana.space/picStore/20201124192424.png

  • 会在终端显示进程号是45352
  • 也会有一个“输出至nohup.out”的提示

键入Ctrl + C,发送SIGINT信号,没反应 && 关闭session,发送SIGHUP信号,也没反应

ID为45352的进程依然存在,后续只能用kill来关闭它。

结论

使用&后台运行程序:

  • 结果会输出到终端
  • 使用Ctrl + C发送SIGINT信号,程序免疫
  • 关闭session发送SIGHUP信号,程序关闭

使用nohup运行程序:

  • 结果默认会输出到nohup.out
  • 使用Ctrl + C发送SIGINT信号,程序关闭
  • 关闭session发送SIGHUP信号,程序免疫

平日线上经常使用nohup和&配合来启动程序:

  • 同时免疫SIGINT和SIGHUP信号

同时,还有一个最佳实践:

  • 不要将信息输出到终端标准输出,标准错误输出,而要用日志组件将信息记录到日志里

扩展

上面的hello去哪了

c语言标准输出有缓冲区,比如printf就存在行缓冲,只有缓冲刷新时才会一次性写入到文件中,我们先使用maven命令来看看效果

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ nohup mvn -v &
[1] 45502
appending output to nohup.out

$ cat nohup.out
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: /Users/david/soft/apache-maven-3.6.3
Java version: 1.8.0_111, vendor: Oracle Corporation, runtime: /Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre
Default locale: zh_CN, platform encoding: UTF-8
OS name: "mac os x", version: "10.16", arch: "x86_64", family: "mac"

重定向

linux中的输入输出文件

  • /dev/null 表示空设备文件
  • 0 表示stdin标准输入
  • 1 表示stdout标准输出
  • 2 表示stderr标准错误

解释

  • file 表示将标准输出输出到file中,也就相当于 1>file
  • 2> error 表示将错误输出到error文件中
  • 2>&1 也就表示将错误重定向到标准输出上
  • 2>&1 >file :错误输出到终端,标准输出重定向到文件file,结果是输出到两个地方; > file 2>&1(标准输出重定向到文件,错误重定向到标准输出),结果是输出到同一个文件。一定要注意顺序,linux在执行shell命令之前,就会确定好所有的输入输出位置,并且从左到右依次执行重定向的命令。

最佳实践

nohup放在命令的开头,表示不挂起(no hang up),也即,关闭终端或者退出某个账号,进程也继续保持运行状态,一般配合&符号一起使用。如nohup command &。

& 放在命令到结尾,表示后台运行,防止终端一直被某个进程占用,这样终端可以执行别到任务,配合 >file 2>&1可以将log保存到某个文件中,但如果终端关闭,则进程也停止运行。如 command > file.log 2>&1 & 。

还以一开始的c程序为例,由于printf存在行缓存,我们使用fprintf将字符串格式化输出到错误输出流文件中,如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#include <stdio.h>
#include <unistd.h>

int main() {
    int i = 0;
    while(1) {
        fprintf(stderr,"hello,%d\n", i++);
        sleep(1);
    }
}

输出结果

http://img.cana.space/picStore/20201124195033.png

nohup的最佳写法:

1
2
3
$ nohup command > myout.file 2>&1 &
eg:
$ nohup ./a > a.out 2>&1 &

先将标准输出重定向到a.out,再将错误输出重定向到标准输出,也就是a.out,这样标准输出和错误输出都重定向到a.out文件,末尾带个&,中断还是挂起都照样运行。

还有一种比较常见的写法nohup command >/dev/null 2>&1 &

/dev/null 表示一个空设备,上面的写法是标准输出重定向到空设备也就是丢弃,再将错误输出也定向到空设备,也就是丢弃所有输出。