美文网首页
如何正确的杀死你的进程

如何正确的杀死你的进程

作者: 捉虫大师 | 来源:发表于2017-03-23 22:34 被阅读99次

    说起杀死进程第一想到的绝对是kill这个命令,看一下man page的介绍

    The kill utility sends a signal to the processes specified by the pid operand(s).

    kill是一个向进程发送信号的命令,为什么进程需要信号呢?假设一个需要运行很久的程序正在运行,这时候你想把它停掉,尴尬,只能拔电源了。据此,linux操作系统提供了一系列的信号,这些信号可在程序运行时发送给进程,可以使用

    kill -l 
    

    来查看有哪些信号,不过常用的不多,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)

    其中

    kill $pid
    

    默认情况下是TERM(15)信号。最常用的是KILL(9)和TERM(15),KILL是强制终止,不敢进程在干什么,都必须终止,而且该信号不能被程序捕获重写,TERM是终止,但是可以被我们的程序捕获重写,这样我们就可以利用它来清理现场了,几乎所有的编程语言都可以捕获kill信号。

    我们来写一段捕获信号处理的代码来做个简单的测试:

    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

    把上面代码修改一下

    
    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;
        }
    
    
    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,除非真的杀不死~

    相关文章

      网友评论

          本文标题:如何正确的杀死你的进程

          本文链接:https://www.haomeiwen.com/subject/ipztottx.html