当前位置: 首页 > 科技观察

老大:连kill-9的原理都不知道,就敢在线执行,明天别用了!

时间:2023-03-23 01:57:19 科技观察

相信很多程序员对Linux系统并不陌生。即使他们日常的开发机器不是linux,但是大部分线上的服务器都是。因此,掌握常用的Linux命令也是程序员必备的技能。但是,恐怕很多人对一些命令都是一知半解,使用不当会导致上线失败。前段时间我们线上应用报错,FGC频繁,急需处理,于是有同事上线重启机器(正常流程应该是先收集heapdump,再重启,这样检查是否存在内存泄漏等问题)。但是在重启过程中,同事发现正常的重启命令应用程序没有反应,于是又尝试使用kill命令“杀死”Java进程,但还是不行。于是他私下决定用“kill-9”结束进程的生命。虽然应用进程被kill掉了,但是很多问题也接踵而至。首先,上游系统突然出现大量告警。对应的开发来了,说调用我们的RPC服务没有响应,经常超时。后来发现系统中有一些脏数据,在同一个事务中有一些需要完全更新的数据只更新了一半...为什么正常的kill不能“杀死”进程,而是kill——9可以吗?为什么kill-9会引发这一系列的连锁反应呢?JVM将如何处理正常的kill执行?要搞清楚这些问题,首先要从kill命令说起。我们都知道kill命令。在Linux中有两种终止进程的方法。如果是前台进程,可以使用Ctrl+C键终止;如果是后台进程,需要使用kill命令终止。(其实Ctrl+C也是kill命令)kill命令的格式为:kill[参数][进程号]如:kill21121kill-921121其中[参数]可选,可以通过进程号jps/ps/pidof/pstree/top等工具获取。kill的命令参数如下:-lsignal,如果不加信号号参数,则使用“-l”参数列出所有信号名-a处理当前进程时,命令名和进程号不限对应关系-p指定kill命令只打印相关进程的进程号,不发送任何信号-s指定发送信号-u指定用户通常,我们使用-l(信号)的次数较多,正如我们上面提到的kill-9中的9是信号。信号如果未指定,则默认发出终止信号(15)。常用信号如下:HUP1终端断开INT2中断(同Ctrl+C)QUIT3退出(同Ctrl+\)TERM15TerminateKILL9强制终止CONT18Continue(与STOP相反,fg/bg命令)STOP19Pause(同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信号只是通知相应的进程执行一个“安全干净的出口”。程序收到信号后,一般会在退出前进行一些“准备工作”,比如资源释放、临时文件清理等,如果准备工作完成,程序就会终止。但是,如果在“准备”过程中,阻塞或其他问题阻碍了成功,应用程序可以选择忽略终止信号。这就是为什么我们有时会使用kill命令来“杀死”应用程序的原因,因为默认的kill信号是SIGTERM(15),而SIGTERM(15)的信号可以被屏蔽和忽略。与kill-15相比,kill-9相对强悍一些。系统会发出一个SIGKILL信号,要求收到该信号的程序立即结束运行,不能被阻塞或忽略。因此,相对于kill-15命令,当kill-9命令执行时,应用程序没有时间进行“准备”,因此这通常会带来一些副作用,比如数据丢失或者终端无法恢复到某个状态。正常状态。我们都知道Java是如何处理SIGTERM(15)的。在Linux中,Java应用程序作为一个独立的进程运行。Java程序的终止是基于JVM的关闭。JVM关闭分为三种:正常关闭:当最后一个非守护线程结束或调用System.exit或通过其他平台特定方法关闭时(接收到SIGINT(2)、SIGTERM(15)信号等)强制关闭:通过调用Runtime.halt方法或在运行过程中强制kill(收到SIGKILL(9)信号)在系统异常关闭:运行过程中遇到RuntimeException等。当JVM进程收到kill-15信号通知时,可以执行一些清理操作,例如删除临时文件。当然,开发者也可以自定义,做一些额外的事情,比如停止tomcat容器,让dubbo服务下线等。而这种自定义JVM清理动作的方式是通过JDK中提供的shutdownhook来实现的。JDK提供了Java.Runtime.addShutdownHook(Threadhook)方法,可以注册一个JVM关闭的钩子。示例如下:packagecom.hollis;publicclassShutdownHookTest{publicstaticvoidmain(String[]args){booleanflag=true;Runtime.getRuntime().addShutdownHook(newThread(()->{System.out.println("hookexecute...");}));while(flag){//appisruning}System.out.println("mainthreadexecuteend...");}}执行命令:?jps6520ShutdownHookTest6521Jps?kill6520控制台输出:hookexecute...Processfinishedwithexitcode143(interruptedbysignal15:SIGTERM)可以看出,当我们使用kill(默认kill-15)关闭进程,程序会先执行我注册的shutdownHook,然后退出,会提示:interruptedbysignal15:SIGTERM如果我们执行命令kill-9:?kill-96520consoleoutput:Processfinishedwithexitcode137(interruptedbysignal9:SIGKILL)可以看出,当我们使用kill-9强制关闭进程时,程序并没有执行shutdownHook,而是直接退出,并且会给出一个提示:interruptedbysignal9:SIGKILL总结kill命令用于终止Linux进程。默认情况下,如果没有指定信号,kill相当于kill-15。当执行kill-15时,系统会向相应的程序发送一个SIGTERM(15)信号。该信号可以被执行、阻塞和忽略。因此,应用程序收到信号后,可以做一些准备工作,然后终止程序。有时,kill-15不能终止程序,因为它可能被忽略。这时可以使用kill-9,系统会发出SIGKILL(9)信号。此信号不允许无知和阻塞,因此应用程序将立即终止。这样也会带来很多副作用,比如数据丢失等,所以在没有必要的时候不要使用kill-9命令,尤其是那些提供RPC服务、执行定时任务、包含长事务的应用,因为kill-9spring容器、tomcat服务器、dubbo服务、流程引擎、状态机等没有足够的时间完成。作者简介:Hollis(ID:hollishuang),对Coding有着独特追求的人,目前是阿里巴巴技术达人,个人技术博主,技术文章全网阅读数千万,合著者《程序员的三门课》。【本文为专栏作家霍利斯原创文章,作者微信公众号Hollis(ID:hollishuang)】点此阅读更多本作者好文