终端断开导致Tomcat进程被kill问题分析
在测试环境中,我们经常会写类似这样的tomcat启动脚本,通过tail日志来检查启动是否成功
|
|
有的时候我们会遇到tomcat进程启动完一段时间后就退出了的情况。通过测试发现,如果不退出脚本就直接关闭终端,tomcat就会在终端关闭后退出
TL;DR
有如下几种解决方式
|
|
|
|
|
|
原因分析
看下tomcat自身的启动脚本,没发现什么特殊的地方
startup.sh通过exec将自己替换为catalina.shcatalina.sh直接以后台方式&启动了java,并且只有当OS为HP-UX时才会加上nohupcatalina.sh退出,java进程ppid变为1
用前面我们自己写的脚本启动一下,观察启动完以后的进程状态
|
|
用strace跟踪一下tomcat进程,然后关闭终端,可以看到是SIGHUP信号导致了tomcat退出
|
|
既然是SIGHUP导致的退出,那么最直接的解决方式自然是用nohup了
那为什么以
&方式后台启动的tomcat会收到SIGHUP信号,但是退出启动脚本后再关闭终端又不会有问题呢
这个问题先放一下,我们尝试退出脚本后再看下进程状态,这时start.sh, tail等进程都结束了,只剩下tomcat
|
|
对比上面的状态图可以看到tomcat进程状态STAT发生了变化,从Sl+变成了Sl
通过man ps我们可以找到+号的意义
+ is in the foreground process group
继续查找原因需要了解一些进程和Job Control相关的知识
理解Unix进程
APUE - Chapter 9. Process Relationships
When does kernel send SIGHUP
Terminally confused (part six)
Job是交互式shell管理进程的一种抽象手段,借此shell可以批量操作进程,例如使用Ctrl-C会停止tail -f foo | grep bar这个job中所有的进程
Job通过进程组Process Group来实现,进程组是进程的集合,Session是进程组的集合,Session可以有一个控制终端Controlling Terminal(ctty)。
- 进程组分为一个前台进程组
foreground process group和多个后台进程组background process groups - 前台进程组接收终端触发的信号,如
SIGINT,以及连接中断时的SIGHUP Session Leader(建立session的进程,例如login shell)接收SIGHUP信号,并决定如何处理。Bash的处理方式是转发给所有的jobs,无论前后台Orphaned Process Group简单来说就是组内进程脱离了和Session Leader的联系的进程组,例如tomcat启动脚本,启动java后自身退出,java成为孤儿进程,ppid为1,进程组也成为孤儿进程组
简单做个实验,启动两组tail+grep的命令
|
|
|
|
可以看到两行命令形成了两个进程组,PGID分别为2085和2087,其中2087是前台进程组(STAT中有+)
用strace跟踪,并输入Ctrl-C,可以看到前台进程组里所有的进程都收到了信号
|
|
关闭终端,后台进程组中的进程收到bash转发的连接中断信号
|
|
现在可以回答前面那个问题了
为什么以
&方式后台启动的tomcat会收到SIGHUP信号,但是退出启动脚本后再关闭终端又不会有问题呢
因为启动脚本未退出时,tomcat位于前台进程组中,退出后tomcat位于孤儿进程组中
继续,为什么启动脚本中所有的命令都在同一个进程组中
因为执行脚本时(非交互shell)默认不启用Job Control,因此脚本内所有命令的PGID都同脚本本身一致
如果在脚本中启用
Job Control会怎样
我们实验下,在脚本中加上set -m,现在每个命令都有了自己的进程组,并且只有tail是前台进程组,那tomcat自然不会收到SIGHUP了
|
|
还有其他途径吗
最彻底的,也是linux中启动守护进程的方法,就是启动tomcat时干脆就不和login shell在一个session里
实验下通过setsid来启动tomcat,计划通~
|
|