Kill之中-9和-15的区别

kill的原理初探

Posted by Haiming on June 14, 2020

kill是Linux之中比较常用的命令之一,那么kill之后带有的参数不同,从而导致的结果不同这一点,自己之前没有详细的了解过。今天看到一篇相关的博文,也算顺带自己做一下梳理和总结。

原文地址

0. 从一个问题引起

作者是从一个问题引起的:一个线上服务总是出现Full GC,这个问题我们之前写过解决方案

总是出现Full GC,很有可能是出现了内存泄露,即使是线上的环境,需要迅速恢复服务到可以使用的状态,也是需要先采集堆dump,然后再重启的,因为这样可以对以后的问题排查起到比较大的帮助。

但是他的同事在正常的重启命令无效之后,发现重启命令没有反应,正常的kill也没有反应,一急之下,直接使用kill -9大招。

这一下子不要紧,使用其的系统其他部分直接报警,对应的开发反应RPC服务超时……而且发现系统之中存在部分脏数据,比如在一个事务之中的数据只更新了一半就没进行了。

那么下面就浅析一下,为什么kill -9 可以杀掉正常kill无法杀掉的进程,以及为什么会引起这一段连锁反应。正常的kill执行的时候,JVM又是怎么处理的。

实际这一块笔者之前有过梳理,是在信号那部分。简而言之,正常我们发出结束信号的时候,只是给程序一个指令,程序知道我们要关闭它,那么就可以将未完成的任务完成之后“优雅关闭”。但是kill -9是直接剥夺这个程序所具有的所有资源,强制关闭,那么进行到一半的任务也好,需要通知上下游的业务也好,就都没有办法进行。

我个人认为,这两个的区别就像: kill 是用遥控器关闭电视,而kill -9 是直接拔电源。

1. kill 命令

想要在Linux 之中终止一个进程,有两种方式:对于前台进程(可以看到屏幕上正在进行的,可能一直在蹦出log的那种),可以直接Ctrl + C 结束(本质发出结束信号)。如果是后台进程,那么就得使用kill 来终止。

kill 命令格式为:

kill [参数] [进程号]
e.g. 
kill -15 22132
kill -9 22132

进程号的获取方式比较多,比如 ps/ top 等等

kill 常用的命令为:

-l 信号,如果不加具体的 l 是几,那么会列出所有的参数,笔者的Windows自带Ubuntu之中示例如下:

 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

下面是几个常用的信号类型:

HUP 1 终端断线
INT 2 中断,和Ctrl+C 一样
QUIT 3 退出,同Ctrl + \ ,终止当前进程并且产生core文件,core文件可以用来还原当时系统状态
KILL 9 强制终止
TERM 15 终止

2. kill -9 和 kill -15的区别

Kill 默认的信号就是15,当使用 kill -15 的时候,系统会发送一个 SIGTERM 信号给对应的程序,当程序接受到信号之后,具体如何处理是自己决定的。比如可以选择:

  1. 立即停止程序
  2. 释放自己占有的资源之后停止程序
  3. 忽略信号,继续执行程序

这也就是我们之前提到过的“优雅退出”。

默认的信号,像我们上面提到过的那样,就是kill -15。 而15这个 SIGTERM 是可以被阻塞或者忽略的。

相比之下,kill -9 在执行的时候,程序会直接被剥夺资源,所以一般都会带来一些副作用,数据丢失等等。

3. Java如何处理SIGTERM(15)的

linux之中,Java 是作为一个独立的进程运行,那么Java程序的运行终止是基于JVM的关闭实现的,一共有三种:

  1. 正常关闭:最后一个非守护线程结束,或者调用了System.exit(),或者通过其他的特定平台的方法关闭(接受SIGINT(2), SIGTERM(15)等等
  2. 强制关闭: 通过调用 Runtime.halt方法,或者是在操作系统之中强制kill(SIGKILL,9)
  3. 异常关闭:运行之中遇到RuntimeException等等异常情况。

那么在正常关闭的情况下,例如JVM进程接受到kill -15 这个信号的时候,是可以做一些清理动作的,比如删除临时文件。开发者此时也可以做一些额外的事情,比如让Tomcat容器停止,dubbo服务下线等等。

这些开发者可以自定义的JVM清理动作的方式,是通过JDK之中的shutdown hook实现的。JDK 提供了Java.Runtime.addShutdownHook(Thread hook)的方法,可以注册一个JVM关闭时候触发的钩子。