补充:JVM 是如何处理信号的

本文是对《从 Java 程序优雅停机到 Linux 信号机制初窥》的补充。

其实到现在为止,已经发现信号机制没那么复杂,本质是通过 sigaction 函数去注册 Handler。接下来就是在源码中走一遍流程。

那么这个流程主要要关注两个,注册和触发(可能在嵌入式领域这两个词描述不太准确),即 Java 程序去注册 Hook(Handler),JVM 收到指定信号后触发 Handler(这个说法不对,不是 JVM 去触发,是内核去调度,但是这一块不在本次探讨范围之内)。

在这之前线简单介绍一下 sigaction 函数(Linux 下 signal 函数就是由 sigaction 函数实现的,这个函数非常重要,涉及到信号的很多知识,过于庞大,本文暂不深入探讨),其原型如下:

 #include <signal.h>
struct  sigaction {
    union __sigaction_u __sigaction_u;  /* signal handler */
    sigset_t sa_mask;               /* signal mask to apply */
    int     sa_flags;               /* see signal options below */
};

int sigaction(int sig, const struct sigaction *restrict act, struct sigaction *restrict oact);

sig 是指信号编号(可以是除了 SIGKILLSIGSTOP 之外的任何信号),sigaction 是具体的处理函数,如果 act 不是空指针,那么就会为 sig 设置新的信号处理函数。

在《从 Java 程序优雅停机到 Linux 信号机制初窥》也已经介绍过了,Java 程序默认会处理 HUPTERMINT 这三种 Signal:

java.lang.System#initializeSystemClass 方法:

// Setup Java signal handlers for HUP, TERM, and INT (where available).
        Terminator.setup();

java.lang.Terminator#setup 方法:

static void setup() {
        if (handler != null) return;
        SignalHandler sh = new SignalHandler() {
            public void handle(Signal sig) {
                Shutdown.exit(sig.getNumber() + 0200);
            }
        };
        handler = sh;
        // When -Xrs is specified the user is responsible for
        // ensuring that shutdown hooks are run by calling
        // System.exit()
        try {
            Signal.handle(new Signal("HUP"), sh);
        } catch (IllegalArgumentException e) {
        }
        try {
            Signal.handle(new Signal("INT"), sh);
        } catch (IllegalArgumentException e) {
        }
        try {
            Signal.handle(new Signal("TERM"), sh);
        } catch (IllegalArgumentException e) {
        }
    }

就是基于 Signal.handle 方法去注册 Handler,接下来就是要将这个方法与 JDK 源码联系起来:

 public static synchronized SignalHandler handle(Signal sig,
                                                    SignalHandler handler)
        throws IllegalArgumentException {
        long newH = (handler instanceof NativeSignalHandler) ?
                      ((NativeSignalHandler)handler).getHandler() : 2;
        long oldH = handle0(sig.number, newH);
        if (oldH == -1) {
            throw new IllegalArgumentException
                ("Signal already used by VM or OS: " + sig);
        }
        signals.put(sig.number, sig);
        synchronized (handlers) {
            SignalHandler oldHandler = handlers.get(sig);
            handlers.remove(sig);
            if (newH == 2) {
                handlers.put(sig, handler);
            }
            if (oldH == 0) {
                return SignalHandler.SIG_DFL;
            } else if (oldH == 1) {
                return SignalHandler.SIG_IGN;
            } else if (oldH == 2) {
                return oldHandler;
            } else {
                return new NativeSignalHandler(oldH);
            }
        }
    }

关注点就要放在 sun.misc.Signal#handle0 这个 native 方法上了。运行 OpenJDK 12,发现已经进入到这里:

在这里插入图片描述

一步步往下走:
在这里插入图片描述

细节方面其实不需要过于关注:
在这里插入图片描述

完整的宏定义如下:

JVM_ENTRY_NO_ENV(void*, JVM_RegisterSignal(jint sig, void* handler))
  // Copied from classic vm
  // signals_md.c       1.4 98/08/23
  void* newHandler = handler == (void *)2
                   ? os::user_handler()
                   : handler;
  switch (sig) {
    /* The following are already used by the VM. */
    case SIGFPE:
    case SIGILL:
    case SIGSEGV:

#if defined(__APPLE__)
    /* On Darwin, memory access errors commonly results in SIGBUS instead
     * of SIGSEGV. */
    case SIGBUS:
#endif

    /* The following signal is used by the VM to dump thread stacks unless
       ReduceSignalUsage is set, in which case the user is allowed to set
       his own _native_ handler for this signal; thus, in either case,
       we do not allow JVM_RegisterSignal to change the handler. */
    case BREAK_SIGNAL:
      return (void *)-1;

    /* The following signals are used for Shutdown Hooks support. However, if
       ReduceSignalUsage (-Xrs) is set, Shutdown Hooks must be invoked via
       System.exit(), Java is not allowed to use these signals, and the the
       user is allowed to set his own _native_ handler for these signals and
       invoke System.exit() as needed. Terminator.setup() is avoiding
       registration of these signals when -Xrs is present.
       - If the HUP signal is ignored (from the nohup) command, then Java
         is not allowed to use this signal.
     */

    case SHUTDOWN1_SIGNAL:
    case SHUTDOWN2_SIGNAL:
    case SHUTDOWN3_SIGNAL:
      if (ReduceSignalUsage) return (void*)-1;
      if (os::Posix::is_sig_ignored(sig)) return (void*)1;
  }

  void* oldHandler = os::signal(sig, newHandler);
  if (oldHandler == os::user_handler()) {
      return (void *)2;
  } else {
      return oldHandler;
  }
JVM_END

欢迎关注公众号
​​​​​​在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值