前言

呵呵 最近看到一篇文章, [讨论] 通过kill -3 pid 来输出Thread Dump日志信息的时效性 

呵呵 关于常见的几个线程 AttchListener, Signal Dispatcher, Reference Handler, Finalizer, 虽然 平时使用的不是很多吧, 但是 还是可以了解一下的 

这里 我们便来看一下 Signal Dispatcher 这个线程吧 

一下的相关代码, 截图如果没有特殊说明 基于 jdk9 

测试用例

package com.hx.test05;

import sun.misc.Signal;
import sun.misc.SignalHandler;

/**
 * SignalHandler
 *
 * @author Jerry.X.He 
 * @version 1.0
 * @date 2020-05-05 10:53
 */
public class Test24SignalHandler {

  // Test24SignalHandler
  public static void main(String[] args) throws Exception {

    Signal.handle(new Signal("ALRM"), new SignalHandler() {
      @Override
      public void handle(Signal signal) {
        System.out.println(signal);
      }
    });

    // 发送信号
//    Signal.raise(new Signal("ALRM"));
    System.in.read();

  }

}

测试代码里面 给 ALRM 这个信号, 注册了一个 signalHandler 

那么 怎么给 vm 发送信号呢?, 通过 kill 命令, kill -14 $pid, 14 号信号, 就是这里的 ALRM 

然后 可以看到 经过 signalHandler 的业务处理, 控制台 输出了 这个信号的相关信息 

Signal Dispatcher

我们来看一下 Signal Dispatcher 的初始化, 以及它 主要做的事情  

25 关于 Signal Dispatcher_java

这里创建了 java.lang.Thread 对象, 并添加到了 系统线程组 里面, 然后下面 还设置了 本地线程, 优先级, 守护线程 的相关配置 

创建了一个 本地线程(C层面), 并将它们互相建立关系, 并启动线程 

Signal Dispatcher 做的事情 

25 关于 Signal Dispatcher_java_02

25 关于 Signal Dispatcher_signal_03

可以看到 就是不断等待 信号过来, 然后进行处理 

对于 SIGBREAK/SIGQUIT 信号, 特殊处理, 输出 堆栈信息, JNI信息, 死锁, 堆的信息 等等 

对于 其他的 信号 将业务委托给 sun.misc.Signal 

sun.misc.Signal 

java 层面的这个 Signal 主要是维护了 Signal 和 java层面的 Signal.Handler 之间的关系, 另外的这个 signals 是辅助使用存放了 Signal 的 number 到 Signal 的映射 

25 关于 Signal Dispatcher_hotspot_04

构造方法, 根据 Signal 的名字, 来查询对应的 Signal, 如果是 未注册的 Signal 抛出异常 

25 关于 Signal Dispatcher_java_05

注册 Signal 和 SignalHandler 的映射, 只保留 java 层面的 handler 

25 关于 Signal Dispatcher_System_06

发送一个信号, 更下面是直接 不同的平台 委托相关的函数发送信号 

25 关于 Signal Dispatcher_java_07

信号的处理, 根据 Signal 的 number 查询 Signal, 然后查询其 注册的 handler, 如果有 启动一个线程 来处理 handler 的业务 

25 关于 Signal Dispatcher_构造方法_08

整理一下流程

1. 程序中注册了 SIGALRM -> alrmSignalHandler 的映射关系 

2. 通过终端 kill 命令 或者 程序中 raise 发送信号 

3. vm 里面 Signal Dispatcher 线程检测到这个信号, 因为不是 SIGBREAK/SIGQUIT, 所以 将信号委托给了 sum.misc.Signal 来处理 

4. sum.misc.Signal 里面查询 SIGALRM 对应的  handler, handler 存在, 接下来新起一个 线程来处理 handler 的业务 

以下运行时截图来自于 jdk8 

25 关于 Signal Dispatcher_hotspot_09

Signal.name 

呵呵 之前将 ALRM 传递为 SIGALRM, 结果在 jdk8 和 jdk9 里面都报错了 

相信通过上面的 jdk9 的 Signal 的构造方法 你很清楚报错的原因是什么, 那么 jdk8 报错的原因呢? 

做映射的 信号量名称 本身就没有 SIG 前缀 

25 关于 Signal Dispatcher_构造方法_10

25 关于 Signal Dispatcher_signal_11

jdk9 的实现是这样子的, 构造方法中限定不能以 SIG 开头, vm 这一层 如果不以 SIG 开头, 添加上 SIG 前缀 

25 关于 Signal Dispatcher_java_05

25 关于 Signal Dispatcher_构造方法_13

HUB, INT, TERM 信号

在 System.initializeSystemClass 的时候 调用了 Terminator.setup 

unix, 类unix 操作系统上面是这样  

25 关于 Signal Dispatcher_构造方法_14

windows 操作系统上面是这样

25 关于 Signal Dispatcher_java_15

注册了这几个信号的 signalHandler, 使用 Shutdown.exit 来关闭系统

Signal already used by VM or OS

mac, unix, 类unix 相关操作系统 上面是这样, 只是可能各个 平台 注册的忽略的信号量不一样 

25 关于 Signal Dispatcher_System_16

windows 上面是这样 

25 关于 Signal Dispatcher_构造方法_17

返回 -1 的情况就回 抛出该异常 

jdk9 上面 name 以 SIG 开头也会抛出该异常 

完 

参考

[讨论] 通过kill -3 pid 来输出Thread Dump日志信息的时效性