前言
nohup为什么要跟着&一起使用,不知道大家有没有思考过其中的小区别,另外很多人会推崇使用screen,但是实际生产环境应用场景有限我就不展开了。这次实际遇到的问题主要是因为使用crontab调用了多个不同的nohup后台执行任务,然而代码逻辑中出现对相同文件的占用导致任务进程产生冲突使得system load负载达到600之高,需要kill任务时也不能盲目操作,需要杀掉的进程多且进程名与其它正常的进程名字有交集,需要合理运用pstree找到问题的源头。
nohup介绍
使用 nohup
运行程序:
- 输出重定向,默认重定向到当前目录下 nohup.out 文件
- 使用 Ctrl + C 发送 SIGINT 信号,程序关闭
- 关闭 Shell Session 发送 SIGHUP 信号,程序免疫
使用 &
运行程序:
- 程序转入后台运行
- 结果会输出到终端
- 使用 Ctrl + C 发送 SIGINT 信号,程序免疫
- 关闭 Shell session 发送 SIGHUP 信号,程序关闭
nohup和&使用实例
一般两个一起组合使用不会受 Ctrl C 和 Shell 关闭的影响:
# 最简单的后台运行
nohup command &
# 输出默认重定向到当前目录下 nohup.out 文件
nohup python main.py &
# 自定义输出文件(标准输出和错误输出合并到 main.log)
nohup python main.py >> main.log 2>&1 &
# 与上一个例子相同作用的简写方法
nohup python main.py &> main.log &
# 不记录输出信息
nohup python main.py &> /dev/null &
# 不记录输出信息并将程序的进程号写入 pidfile.txt 文件中,方便后续杀死进程
nohup python main.py &> /dev/null & echo $! > pidfile.txt
由于使用 nohup 时,会自动将输出写入 nohup.out 文件中,如果文件很大的话,nohup.out 就会不停的增大,我们可以利用 Linux 下一个特殊的文件 /dev/null 来解决这个问题,这个文件就相当于一个黑洞,任何输出到这个文件的东西都将消失 只保留输出错误信息 nohup command >/dev/null 2>log & 所有信息都不要 nohup command >/dev/null 2>&1 &
这里解释一下后面的 2>&1 。 这涉及到 Linux 的重定向,其中 0、1、2 分别是标准输入、标准输出、标准错误输出,用来指定需要重定向的标准输入输出。默认情况下是标出输出,也就是 1 。例如我们而上文提到的 2>&1 是 将错误信息重定向到标准输出。
还有就是如果不想让程序输出,Linux 下有一个 /dev/null 的特殊文件,就像一个黑洞,所有输出到这个文件的信息全部会消失,如果你不需要输出日志,这样做就不会导致输出日志文件越来越大,占用存储空间的问题了
其他相关命令
# 结束当前任务
ctrl+c
# 将一个正在前台执行的命令放到后台,并且处于暂停状态
ctrl+z
# 查看任务,返回任务编号 和 进程号
jobs -l
# 将一个在后台暂停的命令,变成在后台继续执行。如果后台中有多个命令,可以用 bg %jobnumber 将选中的命令调出。
bg %jobnumber
# 将后台中的命令调至前台继续运行。如果后台中有多个命令,可以用 fg %jobnumber(是命令编号,不是进程号)将选中的命令调出
fg %jobnumber
查找后台运行程序
- 已知pid进程号当然最好了
- 使用
ps -ef
或者ps -aux
结合grep
过滤 - 使用
pstree -p
确认复杂进程树结构 - 使用
lsof -i:80
查端口获得进程号 - 使用
netstat -anp | grep 80
查端口获得进程号,推荐使用lsof
杀死后台运行程序
kill 命令用来删除执行中的程序或工作。kill 可将指定的信息送至程序。预设的信息为 SIGTERM (15), 可将指定程序终止。若仍无法终止该程序,可使用 SIGKILL (9) 信息尝试强制删除程序,即 kill -9。程序或工作的编号可利用 ps 指令或 job 指令查看。
语法 kill(选项)(参数)
选项
-a:当处理当前进程时,不限制命令名和进程号的对应关系;
-l <信息编号>:若不加<信息编号>选项,则-l参数会列出全部的信息名称;
-p:指定kill 命令只打印相关进程的进程号,而不发送任何信号;
-s <信息名称或编号>:指定要送出的信息;
-u:指定用户。
先用 ps 查找进程,然后用 kill 杀掉:
ps -ef | grep vim
root 3268 2884 0 16:21 pts/1 00:00:00 vim install.log
root 3370 2822 0 16:21 pts/0 00:00:00 grep vim
kill 3268
kill 3268
-bash: kill: (3268) - 没有那个进程
使用pstree查找和杀死复杂进程
常见的3个kill命令 - kill - pkill - killall
搭配查找进程命令 - pidof - pstree
# 已知进程号,启动时输出后台运行程序的进程号,然后读取进程号杀死后台程序:
kill -9 `cat pidfile.txt`
# 进程数量较多且有规律,不和其他正常进程冲突
killall 进程名
kill -9 $(pidof 进程名)
# 进程数量多,规律不明显,混了正常进程
pstree -p
# 复杂点的情况比如像我遇到的真实案例
|-crond(127436)-+-crond(138887)---bash(138892)---bash(138895)---grep(140604)
| |-crond(139310)---bash(139323)---bash(139324)---python(139431)
| |-crond(139311)---bash(139325)---bash(139331)---python(139452)
| |-crond(139312)---bash(139318)---bash(139319)---python(139442)
| |-crond(139313)---bash(139317)---bash(139320)---python(139444)
| |-crond(139314)---bash(139329)---bash(139340)---python(139443)
| |-crond(139315)---bash(139327)---bash(139339)---grep(140768)
| |-crond(139651)---bash(139660)---bash(139661)---python(139915)
| |-crond(139652)---bash(139664)---bash(139666)---python(139916)
| |-crond(139653)---bash(139663)---bash(139665)---python(139914)
| |-crond(139654)---bash(139675)---bash(139683)---python(139918)
| |-crond(139655)---bash(139668)---bash(139677)---python(139913)
| `-crond(139656)---bash(139669)---bash(139682)---grep(139780)
# 如果你喜欢用grep也没有问题,多设置几个过滤条件语句组合在一起即可
ps -ef | grep 'python' | grep -v grep | awk '{print $2}' |xargs kill -9