相关系列文章:
一 进程状态转换
二 kill命令回顾
kill :发送指定的信号到相应进程。不指定信号将发送SIGTERM(15)终止指定进程。若仍无法终止该程序可用“-KILL” 参数,其发送的信号为SIGKILL(9) ,将强制结束进程,使用ps命令或者jobs 命令可以查看进程号。root用户将影响用户的进程,非root用户只能影响自己的进程。
2.1 kill命令参数
在命令行输入kill,可以看到参数提示如下:
kill: usage: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec]
参数说明:
-l <信号编号>,若不加信号的编号参数,则使用“-l”参数会列出全部的信号名称。常用的kill -15,kill -9这里的9 和 15就是信号;
-a 当处理当前进程时,不限制命令名和进程号的对应关系;
-p 指定kill 命令只打印相关进程的进程号,而不发送任何信号;
-s 指定发送信号;
-u 指定用户;
2.2 信号
使用kill -l 可以列举所有支持的信号:
[admin@test-3 ~]$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
也可以通过kill -l SIGHUP 查指定信号对应的数值:
[admin@test-3 ~]$ kill -l SIGHUP
1
几个常用的信号如下:
HUP 1 终端断线
INT 2 中断(同 Ctrl + C)
QUIT 3 退出(同 Ctrl + \)
TERM 15 终止
KILL 9 强制终止
CONT 18 继续(与STOP相反, fg/bg命令)
STOP 19 暂停(同 Ctrl + Z)
其中,信号如果没有指定的话,默认会发出终止信号(15)。比较常用的就是强制终止信号:9
和终止信号:15
,另外,中断信号:2
其实就是Ctrl + C结束前台进程。
2.3 kill -15与kill -9
当我们使用kill pid时,实际相当于kill -15 pid。也就是说默认信号为15。使用kill -15
时,系统会发送一个SIGTERM的信号给对应的程序。当程序接收到该信号后,具体要如何处理自己可以决定。
这时候,应用程序可以选择:
-
1、立即停止程序
-
2、释放响应资源后停止程序
-
3、忽略该信号,继续执行程序
因为kill -15
信号只是通知对应的进程要进行"安全、干净的退出",程序接到信号之后,退出前一般会进行一些"准备工作",如资源释放、临时文件清理等等,如果准备工作做完了,再进行程序的终止。
但是,如果在"准备工作"进行过程中,遇到阻塞或者其他问题导致无法成功,那么应用程序可以选择忽略该终止信号。
这也就是为什么我们有的时候使用kill命令是没办法"杀死"应用的原因,因为默认的kill信号是SIGTERM(15),而SIGTERM(15)的信号是可以被阻塞和忽略的。
和kill -15
相比,kill -9
就相对强硬得多,系统会发出SIGKILL信号,他要求接收到该信号的程序应该立即结束运行,不能被阻塞或者忽略。
所以,kill -9在执行时,应用程序是没有时间进行"准备工作"的,所以这通常会带来一些副作用,数据丢失或者终端无法恢复到正常状态等。
三 Java应用对SIGTERM信号的处理
Java应用在Linux中是以一个独立进程的形式运行的,Java程序的终止运行基于JVM的关闭实现,JVM关闭方式分为3种:
正常关闭:当最后一个非守护线程结束或者调用了System.exit或者通过其他特定平台的方法关闭(接收到SIGINT(2)、SIGTERM(15)信号等)
强制关闭:通过调用Runtime.halt方法或者是在操作系统中强制kill(接收到SIGKILL(9)信号)
异常关闭:运行中遇到RuntimeException异常等。
JVM进程在接收到kill -15
信号通知的时候,会做一些清理动作的,例如删除临时文件。同时,也提供了hook机制,来让开发者自定义清理动作,对应的方法为:Java.Runtime.addShutdownHook(Thread hook)。我们可以自定义一个shuthook并测试效果:
package shutdown;
import java.util.concurrent.TimeUnit;
public class MyShutdownHook {
public static void main(String[] args) throws InterruptedException{
boolean flag = true;
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("my hook execute end");
}));
while (flag) {
System.out.println("my app is running");
TimeUnit.SECONDS.sleep(3);
}
System.out.println("main thread execute end");
}
}
在IDE内执行后,进程会一直存在,我们在其中加了一个死循环保证进程常驻,且每3秒钟打印一次app正在运行中。通过jps命令查看进程id,并执行kill命令,查看执行效果:
192:dubbo-proxy-tools xxx$ jps
24272
6178 Launcher
6179 MyShutdownHook
6182 Jps
99256 RemoteMavenServer
585 UserClient
24446 war
192:dubbo-proxy-tools xxx$ kill 6179
其中6179 MyShutdownHook是我们的这个程序运行进程。执行kill后可以看到程序运行控制台的输出如下:
my app is running
my app is running
my app is running
my hook execute end
Process finished with exit code 143 (interrupted by signal 15: SIGTERM)
可见hook被执行,并且给了接收到signal 15信号的提示后,线程中断,所以没有执行结尾的打印主线程退出记录(第13行的System.out.println("main thread execute end");)。
再次运行程序,我们来看看kill -9执行的效果:
my app is running
my app is running
Process finished with exit code 137 (interrupted by signal 9: SIGKILL)
可见kill -9 强杀进程后,没有执行shutdownHook,而是直接退出。给出的提示为:interrupted by signal 9: SIGKILL