Linux命令kill-9的原理
相信很多程序员对于Linux系统都不陌⽣,即使⾃⼰的⽇常开发机器不是Linux,那么线上服务器也⼤部分都是的,所以,掌握常⽤的Linux 命令也是程序员必备的技能。
但是,怕就怕很多⼈对于部分命令只是⼀知半解,使⽤不当就能导致线上故障。
前段时间,我们的线上应⽤报警,频繁FGC,需要紧急处理问题,于是有同事去线上重启机器(正常程序应该是先采集堆dump,然后再重启,⽅便排查是否存在内存泄露等问题)。
但是在重启过程中,同事发现正常的重启命令应⽤⽆反应,然后尝试使⽤kill命令"杀"掉Java进程,但是仍然⽆效。于是他私⾃决定使⽤ "kill -9"结束了进程的⽣命。
虽然应⽤进程被⼲掉了,但是随之⽽来带来了很多问题,⾸先是上游系统突然发⽣⼤量报警,对应开发过来说调⽤我们的RPC服务⽆响应,频繁超时。
后来,我们⼜发现系统中存在部分脏数据,有些在同⼀个事务中需要完整更新的数据,只更新了⼀半…
为什么正常的kill⽆法"杀掉"进程,⽽kill -9就可以?为什么kill -9会引发这⼀连串连锁反应?正常的kill执⾏时,JVM会如何处理的呢?
要搞清楚这些问题,我们要先从kill命令说起。
kill 命令
我们都知道,想要在Linux中终⽌⼀个进程有两种⽅式,如果是前台进程可以使⽤Ctrl+C键进⾏终⽌;如果是后台进程,那么需要使⽤kill命令来终⽌。(其实Ctrl+C也是kill命令)
kill命令的格式是:
kill[参数][进程号]
如:
kill 90124
kill-9  90124
其中[参数]是可选的,进程号可以通过jps/ps/pidof/pstree/top等⼯具获取。
kill的命令参数有以下⼏种:
-l 信号,若果不加信号的编号参数,则使⽤“-l”参数会列出全部的信号名称
-a 当处理当前进程时,不限制命令名和进程号的对应关系
-p 指定kill命令只打印相关进程的进程号,⽽不发送任何信号
-s 指定发送信号
-u 指定⽤户
通常情况下,我们使⽤的-l(信号)的时候⽐较多,如我们前⽂提到的kill -9中的9就是信号。
信号如果没有指定的话,默认会发出终⽌信号(15)。常⽤的信号如下:
HUP 1 终端断线
INT 2 中断(同 Ctrl + C)
QUIT 3 退出(同 Ctrl + \)
TERM 15 终⽌
KILL 9 强制终⽌
CONT 18 继续(与STOP相反, fg/bg命令)
STOP 19 暂停(同 Ctrl + Z)
⽐较常⽤的就是强制终⽌信号:9和终⽌信号:15,另外,中断信号:2其实就是我们前⽂提到的Ctrl + C结束前台进程。
那么,kill -9 和 kill -15到底有什么区别呢?该如何选择呢?
kill -9 和 kill -15的区别
kill命令默认的信号就是15,⾸先来说⼀下这个默认的kill -15信号。
当使⽤kill -15时,系统会发送⼀个SIGTERM的信号给对应的程序。当程序接收到该信号后,具体要如何处理是⾃⼰可以决定的。
这时候,应⽤程序可以选择:
1、⽴即停⽌程序
2、释放响应资源后停⽌程序
3、忽略该信号,继续执⾏程序
因为kill -15信号只是通知对应的进程要进⾏"安全、⼲净的退出",程序接到信号之后,退出前⼀般会进⾏⼀些"准备⼯作",如资源释放、临时⽂件清理等等,如果准备⼯作做完了,再进⾏程序的终⽌。
linux用户系统相关命令但是,如果在"准备⼯作"进⾏过程中,遇到阻塞或者其他问题导致⽆法成功,那么应⽤程序可以选择忽略该终⽌信号。
这也就是为什么我们有的时候使⽤kill命令是没办法"杀死"应⽤的原因,因为默认的kill信号是SIGTERM(15),⽽SIGTERM(15)的信号是可以被阻塞和忽略的。
和kill -15相⽐,kill -9就相对强硬⼀点,系统会发出SIGKILL信号,他要求接收到该信号的程序应该⽴即结束运⾏,不能被阻塞或者忽略。
所以,相⽐于kill -15命令,kill -9在执⾏时,应⽤程序是没有时间进⾏"准备⼯作"的,所以这通常会带来⼀些副作⽤,数据丢失或者终端⽆法恢复到正常状态等。
Java是如何处理SIGTERM(15)
我们都知道,在Linux中,Java应⽤是作为⼀个独⽴进程运⾏的,Java程序的终⽌运⾏是基于JVM的关闭实现的,JVM关闭⽅式分为3种:
正常关闭:当最后⼀个⾮守护线程结束或者调⽤了it或者通过其他特定平台的⽅法关闭(接收到SIGINT(2)、
SIGTERM(15)信号等)
强制关闭:通过调⽤Runtime.halt⽅法或者是在操作系统中强制kill(接收到SIGKILL(9)信号)
异常关闭:运⾏中遇到RuntimeException异常等
JVM进程在接收到kill -15信号通知的时候,是可以做⼀些清理动作的,⽐如删除临时⽂件等。
当然,开发者也是可以⾃定义做⼀些额外的事情的,⽐如让tomcat容器停⽌,让dubbo服务下线等。
⽽这种⾃定义JVM清理动作的⽅式,是通过JDK中提供的shutdown hook实现的。JDK提供了
Java.Runtime.addShutdownHook(Thread hook)⽅法,可以注册⼀个JVM关闭的钩⼦。
public class ShutdownHookTest {
public static void main(String[] args){
boolean flag =true;
System.out.println("");
}));
while(flag){
// app is runing
}
System.out.println("main thread ");
}
}
执⾏命令:
➜ jps
6520 ShutdownHookTest
6521 Jps
➜ kill 6520
控制台输出内容:
Process finished with exit code 143(interrupted by signal 15: SIGTERM)
可以看到,当我们使⽤kill(默认kill -15)关闭进程的时候,程序会先执⾏我注册的shutdownHook,然后再退出,并且会给出⼀个提⽰:interrupted by signal 15: SIGTERM
如果我们执⾏命令kill -9:
kill -96520
控制台输出内容:
Process finished with exit code 137(interrupted by signal 9: SIGKILL)
可以看到,当我们使⽤kill -9 强制关闭进程的时候,程序并没有执⾏shutdownHook,⽽是直接退出了,并且会给出⼀个提⽰:interrupted by signal 9: SIGKILL
总结
kill命令⽤于终⽌Linux进程,默认情况下,如果不指定信号,kill 等价于kill -15。
kill -15执⾏时,系统向对应的程序发送SIGTERM(15)信号,该信号是可以被执⾏、阻塞和忽略的,所以应⽤程序接收到信号后,可以做⼀些准备⼯作,再进⾏程序终⽌。
有的时候,kill -15⽆法终⽌程序,因为他可能被忽略,这时候可以使⽤kill -9,系统会发出SIGKILL(9)信号,该信号不允许忽略和阻塞,所以应⽤程序会⽴即终⽌。
这也会带来很多副作⽤,如数据丢失等,所以,在⾮必要时,不要使⽤kill -9命令,尤其是那些web应⽤、提供RPC服务、执⾏定时任务、包含长事务等应⽤中,因为kill -9 没给spring容器、tomcat服务器、dubbo服务、流程引擎、状态机等⾜够的时间进⾏收尾。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。