Linux kill & Java shutdownhook

shutdown-hook

  • 建议加上shutdown的钩子
    • 如果程序出现了内存溢出crash 则现在代码是没有任何保护措施的
    • 或者说运维不小心关闭了服务器等
    • 或者运维不小心kill了游戏服务器进程等
  • 无法避免kill -9
  • 时机
    • 程序正常退出
    • 使用System.exit()
    • 终端使用Ctrl+C触发的中断
      + 系统关闭
    • OutOfMemory宕机
    • 使用Kill pid命令干掉进程(注:在使用kill -9 pid时,是不会被调用的)

kill

  • kill默认信号是SIGTERM
    • 15 终止信号
  • kill -9
    • SIGKILL kill信号
  • kill -2
    • SIGINT interrupt 中断信号
    • 同ctrl-c
  • kill -1
    • SIGHUP hang up 挂起信号
  • kill -3
    • SIGQUIT 可打印进程#线程堆栈
  • 只有kill -9能够结束jvm进程,别的信号量只是发送给java进程处理,至于如何响应是程序代码决定的
    • SIGTERM是不带参数时kill发送的信号,意思是进程终止运行,但执行与否还得看进程是否支持.如果进程还没有终止,可以使用 kill -SIGKILL pid,这是由内核来终止进程,进程不能监听这个信号
    • Java程序如果添加了shutdownhook,则可以监听1/2/15

linux

  • screen
  • SIGHUP与nohup
    • 当用户启动一个进程的时候,这个进程是运行在前台,使用与相应控制终端相联系的标准输入、输出进行输入和输出。即使将进程的输入输出重定向,并将进程放在后台执行,进程仍然和当前终端设备有关系。正因为如此,在当前的登录会话结束时,控制终端设备将和登录进程相脱离,那么系统就向所有与这个终端相联系的进程发送SIGHUP的信号,通知进程线路已经挂起了,如果程序没有接管这个信号的处理,那么缺省的反应是进程结束。因此普通的程序并不能真正脱离登录会话而运行进程,为了使得在系统登录后还可以正常执行,只有使用命令nohup来启动相应程序

测试kill与Java#shutdownhook与nohup

  • 主要测试比较不常见的kill#-1,#-2,#-3选项
  • 测试代码
public class ShutdownHookTest {
    public static void main(String[] args) throws Exception {
        Runtime.getRuntime()
                .addShutdownHook(new Thread(() -> System.out.println("ShutdownHookTest")));

        while (true) {
            Thread.sleep(5000);
        }
    }
}
  • 测试步骤
    • $ javac javac ShutdownHookTest.java // 编译,生成.class
    • $ java ShutdownHookTest // 前台运行
    • $ jps // 切换到另一个session,查询进程pid
    • $ kill -1 1729 // 使用kill -1 发出HUP的信号
      • 进程结束
      • 控制台输出:ShutdownHookTest 即钩子可监听SIGHUP(1)
      • 解释:因为进程本身就是非nohup运行 则HUP信号直接结束该进程(可参考nohup命令)
        • 如果用nohup启动(会忽略SIGHUP信号) 则kill -1则无效 即进程没有结束
        • 因为进程没结束 所以也不会执行shutdownhook
    • $ kill -2 1729 // 使用kill -2 发出INTTERUPT中断信号
      • 控制台输出:ShutdownHookTest 即钩子可监听SIGINT(2)
      • 进程退出的原因是发送了一个中断信号,而我们的Java程序核心逻辑是sleep,sleep被中断,进程结束
        • 只能中断前台进程
        • 如果我们是用nohup & 启动,即后台进程则无法中断
        • nohup表示忽略SIGHUP &表示后台进程
    • $ kill -3 1729 // 使用kill -3 发出SIGQUIT信号
      • 直接打印出了进程#线程堆栈
          2017-09-01 15:38:29
          Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.40-b25 mixed mode):
          ...
      
      • 即该命令可以直接打印进程的线程堆栈信息(又多了一个技能包) 前台进程也没有结束
      • 如果是用nohup启动的 则默认会将线程堆栈信息写入nohup.out
        • 如果你将nohup日志重定向到了/dev/null 则无法看到输出
    • $ kill -15 1729 // 使用kill -15 发出SIGTERM信号 终止信号(15是kill的默认信号)
      • 控制台输出:ShutdownHookTest 即钩子可监听SIGTERM
      • 进程退出 原因同 -2
      • 前台进程和后台进程都可终止
    • $ kill -9 1729 // 使用kill -9 发出SIGKILL信号 kill信号
      • 控制台直接显示Killed字样 shutdownhook的输出没有执行
      • 进程退出
      • 即钩子无法监听9
  • 其他
    • 只有kill -9能够结束jvm进程,别的信号量只是发送给java进程处理,至于如何响应是程序代码决定的
    • kill -3 可以打印线程堆栈
    • kill -1 对于nohup启动的进程无法终止,因为nohup忽略该信号
    • kill -2 中断前台进程
    • kill -15 前后台进程都可以终止
    • kill -9 死亡kill
    • shell中使用& 可避免终端关闭因SIGHUP信号而终止
      • 非nohup & 可使用kill -1 或者kill -15 或者kill终止
      • 不能使用kill -2终止,因为&表示后台进程 无法关闭
    • 即kill -1/-2/-15 SIGHUP/SIGINT/SIGTERM 都可以将进程关闭
    • 而Java的shutdownhook也恰恰可以监听1/2/15
    • 归根结底,关服最稳妥的方法就是,用http触发关服方法,同时在钩子里也触发这个方法,业务中关服了开服需要继续的,业务单独实现入库
  • 其他测试
    • 如果我们的程序的主逻辑是一个等待标准输入的逻辑
      • 1/2/15都可以终止程序
      • ctrl+c也可以终止
      • 如果直接用nohup启动的话则会直接抛出异常
        • If standard input is associated with a terminal, the nohup utility may redirect standard input from an unspecified file.
        • standard input get redirected from /dev/null by nohup
        • 关闭标准输入 该进程不再能够接收任何输入,即使运行在前台
        • nohup命令安排来自/dev/null的输入,并且输出和错误都可以转到nohup.out
      • 当前与终端交互的进程称为前台进程组,其余进程组称为后台进程组
      • 相对而言nohup占用资源更少,没有交互需求的时候,nohup就能满足需要了
        • !! 如果需要交互逻辑需要用screen/tmux
    • jvm有一个-Xrs的参数(reduce signal),用来忽略系统信号
  • ref
  • 扩展-why nohup redirect stanard input
    Historical versions of nohup did not affect standard input, but that causes problems in the common scenario where the user logs into a system, types the command:
    
    nohup make &
    
    at the prompt, and then logs out. If standard input is not affected by nohup, the login session may not terminate for quite some time, since standard input remains open until make exits. To avoid this problem, POSIX.1-2008 allows implementations to redirect standard input if it is a terminal. Since the behavior is implementation-defined, portable applications that may run into the problem should redirect standard input themselves. For example, instead of:
    
    nohup make &
    
    an application can invoke:
    
    nohup make </dev/null &
The redirection of stdin for nohup is entirely optional as nohup will usually redirect it to </dev/null if it was a terminal, to avoid the terminal not being able to close when you type exit. You can do your own 0< redirection, to /dev/null or a file, to avoid this.

推荐阅读更多精彩内容