说起杀死进程第一想到的绝对是kill这个命令,看一下man page的介绍
The kill utility sends a signal to the processes specified by the pid operand(s).
kill是一个向进程发送信号的命令,为什么进程需要信号呢?假设一个需要运行很久的程序正在运行,这时候你想把它停掉,尴尬,只能拔电源了。据此,linux操作系统提供了一系列的信号,这些信号可在程序运行时发送给进程,可以使用
来查看有哪些信号,不过常用的不多,man page里面介绍的常用的信号有如下几种
- 1 HUP (hang up)
- 2 INT (interrupt)
- 3 QUIT (quit)
- 6 ABRT (abort)
- 9 KILL (non-catchable,non-ignorable kill)
- 14 ALRM (alarm clock)
- 15 TERM (software termination signal)
其中
默认情况下是TERM(15)信号。最常用的是INT(2)和TERM(15),INT是强制终止,不敢进程在干什么,都必须终止,而且该信号不能被程序捕获重写,TERM是终止,但是可以被我们的程序捕获重写,这样我们就可以利用它来清理现场了,几乎所有的编程语言都可以捕获kill信号。
我们来写一段捕获信号处理的代码来做个简单的测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| import sun.misc.Signal; import sun.misc.SignalHandler; public class KillTest implements SignalHandler { private int i = 0; public static void main(String[] args) { SignalTest signalTest = new SignalTest(); Signal.handle(new Signal("HUP"), signalTest); Signal.handle(new Signal("INT"), signalTest); //Signal.handle(new Signal("QUIT"), signalTest);// 该信号不能捕获 Signal.handle(new Signal("ABRT"), signalTest); //Signal.handle(new Signal("KILL"), signalTest);// 该信号不能捕获 Signal.handle(new Signal("ALRM"), signalTest); Signal.handle(new Signal("TERM"), signalTest); while (true) { killTest.incrI(); System.out.print(killTest.getI()); try { Thread.sleep(1000L); } catch (InterruptedException e) { e.printStackTrace(); } } } public int getI() { return this.i; } public void setI(int i) { this.i = i; } public int incrI() { this.i++; return this.i; } @Override public void handle(Signal signal) { System.out.printf("receive signal " + signal.getName() + "-" + signal.getNumber()); } }
|
在常用的信号中QUIT和KILL是不能捕获的,也就是如果别人执行了这个kill,那么你的进程立马就得死,没得商量,这也可以理解,如果有人恶意的把所有信号都捕获且忽略了,那想杀掉这个进程那不是只能拔电源了?
那么这种捕获通常有些什么用处呢?
最常用的像发布代码,发布java代码一般是先把新包拉到服务器上,备份旧包,停止服务(如摘除节点),杀掉进程,新包替换旧包,拉起新进程,提供服务。
如果原来的进程有一个文件锁,这时杀掉了原进程(假设被杀死时不做任何处理,或者使用kill -9),文件锁还在,那新的进程拉起来后一直处于异常状态,因为原来的锁一直在,没人可以解掉。
刚刚如果你捕获了kill -15 信号并清理了锁文件,恰好发布系统也是使用kill -15,那么程序很完美。
假设不幸用了kill -9 那也无能为力了,但是这毕竟是我们自己的程序,你应该知道怎么杀死它,所以,修改一下发布脚本吧,别那么粗暴。
当然在java中可以不用捕获信号来处理,我们可以利用更简单方便的ShutdownHook
把上面代码修改一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| public class KillTest { private int i = 0; public static void main(String[] args) { KillTest killTest = new KillTest(); Runtime.getRuntime().addShutdownHook( new ShutDownThread(killTest) ); while (true) { killTest.incrI(); System.out.print(killTest.getI()); try { Thread.sleep(1000L); } catch (InterruptedException e) { e.printStackTrace(); } } } public int getI() { return this.i; } public void setI(int i) { this.i = i; } public int incrI() { this.i++; return this.i; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class ShutDownThread extends Thread { public KillTest killTest; public ShutDownThread(KillTest killTest) { this.killTest = killTest; } public int getKillTestI() { return killTest.getI(); } @Override public void run() { super.run(); int i = getKillTestI(); System.out.print("\nI'm shutdown hook..."+i); } }
|
通过这种方法就不用注册那么多信号了,毕竟能捕获的那么的,我猜测这个的实现也是基于信号的封装,但是这里有一个注意的地方是,这个hook在程序正常退出,异常退出(如内存不够等)也会执行,正常退出在上面的例子中得做一个简单的区分。
最后总结一下,如果程序退出需要处理一些东西,捕获一下信号或者加个hook,说不定谁会去服务器上执行一下kill呢,就算没有,也要防止类似发布系统等对你程序的影响。执行kill最好也不要kill -9,除非真的杀不死~