HotSpot JVM中的signal
Posted 底层技术研究
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HotSpot JVM中的signal相关的知识,希望对你有一定的参考价值。
本文将会从源码角度看下HotSpot JVM中是如何处理signal的。有关signal机制的简单介绍,可以看下另外一篇文章: 。
OpenJDK源码的版本:
➜ jdk hg id
76072a077ee1 jdk-11+28
在JVM启动的过程代码中,我们首先会找到 SR_initialize 方法
C++文件src/hotspot/os/linux/os_linux.cpp
// Signal number used to suspend/resume a thread
static int SR_signum = SIGUSR2;
...
static int SR_initialize() {
struct sigaction act;
char *s;
// Get signal number to use for suspend/resume
if ((s = ::getenv("_JAVA_SR_SIGNUM")) != 0) {
int sig = ::strtol(s, 0, 10);
if (sig > MAX2(SIGSEGV, SIGBUS) &&
sig < NSIG) {
SR_signum = sig;
} else {
warning(...);
}
}
...
act.sa_flags = SA_RESTART|SA_SIGINFO;
act.sa_handler = (void (*)(int)) SR_handler;
...
if (sigaction(SR_signum, &act, 0) == -1) {
return -1;
}
...
return 0;
}
该方法为SIGUSR2信号(可通过_JAVA_SR_SIGNUM环境变量修改)设置了处理函数SR_handler,用于支持线程的suspend/resume。
接着,我们会找到 os::Linux::install_signal_handlers 方法
C++文件src/hotspot/os/linux/os_linux.cpp
// install signal handlers for signals that HotSpot needs to
// handle in order to support Java-level exception handling.
void os::Linux::install_signal_handlers() {
if (!signal_handlers_are_installed) {
signal_handlers_are_installed = true;
...
set_signal_handler(SIGSEGV, true);
set_signal_handler(SIGPIPE, true);
set_signal_handler(SIGBUS, true);
set_signal_handler(SIGILL, true);
set_signal_handler(SIGFPE, true);
#if defined(PPC64)
set_signal_handler(SIGTRAP, true);
#endif
set_signal_handler(SIGXFSZ, true);
...
}
}
该方法涉及到了很多signal,而且都是调用set_signal_handler方法设置的handler,继续看下这个方法
C++文件src/hotspot/os/linux/os_linux.cpp
void os::Linux::set_signal_handler(int sig, bool set_installed) {
...
struct sigaction sigAct;
sigfillset(&(sigAct.sa_mask));
sigAct.sa_handler = SIG_DFL;
if (!set_installed) {
sigAct.sa_flags = SA_SIGINFO|SA_RESTART;
} else {
sigAct.sa_sigaction = signalHandler;
sigAct.sa_flags = SA_SIGINFO|SA_RESTART;
}
...
int ret = sigaction(sig, &sigAct, &oldAct);
assert(ret == 0, "check");
...
}
根据set_signal_handler方法我们可以看到,install_signal_handlers方法为SIGSEGV、SIGPIPE等很多signal都设置了同样的处理函数signalHandler,用于实现Java-level exception handling。
继续JVM的启动代码,我们会找到 os::initialize_jdk_signal_support 方法,这个方法以及其相关逻辑也是这篇文章要重点讲解的内容。
C++文件src/hotspot/share/runtime/os.cpp
void os::initialize_jdk_signal_support(TRAPS) {
if (!ReduceSignalUsage) {
// Setup JavaThread for processing signals
const char thread_name[] = "Signal Dispatcher";
Handle string = java_lang_String::create_from_str(thread_name, CHECK);
// Initialize thread_oop to put it into the system threadGroup
Handle thread_group (THREAD, Universe::system_thread_group());
Handle thread_oop = JavaCalls::construct_new_instance(SystemDictionary::Thread_klass(),
vmSymbols::threadgroup_string_void_signature(),
thread_group,
string,
CHECK);
...
{ MutexLocker mu(Threads_lock);
JavaThread* signal_thread = new JavaThread(&signal_thread_entry);
...
java_lang_Thread::set_thread(thread_oop(), signal_thread);
...
Thread::start(signal_thread);
}
// Handle ^BREAK
os::signal(SIGBREAK, os::user_handler());
}
}
该方法启动了一个名叫Signal Dispatcher的线程,其入口函数为signal_thread_entry。然后为SIGBREAK信号(其实就是SIGQUIT信号)注册了处理函数os::user_handler()。
看下 signal_thread_entry 入口函数
C++文件src/hotspot/share/runtime/os.cpp
static void signal_thread_entry(JavaThread* thread, TRAPS) {
os::set_priority(thread, NearMaxPriority);
while (true) {
int sig;
{
...
sig = os::signal_wait();
}
if (sig == os::sigexitnum_pd()) {
// Terminate the signal thread
return;
}
switch (sig) {
case SIGBREAK: {
// Check if the signal is a trigger to start the Attach Listener - in that
// case don't print stack traces.
if (!DisableAttachMechanism && AttachListener::is_init_trigger()) {
continue;
}
// Print stack traces
// Any SIGBREAK operations added here should make sure to flush
// the output stream (e.g. tty->flush()) after output. See 4803766.
// Each module also prints an extra carriage return after its output.
VM_PrintThreads op;
VMThread::execute(&op);
VM_PrintJNI jni_op;
VMThread::execute(&jni_op);
VM_FindDeadlocks op1(tty);
VMThread::execute(&op1);
Universe::print_heap_at_SIGBREAK();
if (PrintClassHistogram) {
VM_GC_HeapInspection op1(tty, true /* force full GC before heap inspection */);
VMThread::execute(&op1);
}
if (JvmtiExport::should_post_data_dump()) {
JvmtiExport::post_data_dump();
}
break;
}
default: {
// Dispatch the signal to java
HandleMark hm(THREAD);
Klass* klass = SystemDictionary::resolve_or_null(vmSymbols::jdk_internal_misc_Signal(), THREAD);
if (klass != NULL) {
JavaValue result(T_VOID);
JavaCallArguments args;
args.push_int(sig);
JavaCalls::call_static(
&result,
klass,
vmSymbols::dispatch_name(),
vmSymbols::int_void_signature(),
&args,
THREAD
);
}
...
}
}
}
}
该方法大体的处理逻辑为,等待signal,然后看signal是否是SIGBREAK(即SIGQUIT),如果是就启动attach listener或者打印各种信息,比如线程堆栈信息。如果不是,就会调用Java类jdk.internal.misc.Signal的dispatch方法,交给其进一步处理。
看下dispatch方法
Java类jdk.internal.misc.Signal
/* Called by the VM to execute Java signal handlers. */
private static void dispatch(final int number) {
final Signal sig = signals.get(number);
final Signal.Handler handler = handlers.get(sig);
Runnable runnable = new Runnable () {
public void run() {
// Don't bother to reset the priority. Signal handler will
// run at maximum priority inherited from the VM signal
// dispatch thread.
// Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
handler.handle(sig);
}
};
if (handler != null) {
new Thread(null, runnable, sig + " handler", 0, false).start();
}
}
该方法就是根据signal获取其handler,然后开启一个新的Java线程执行这个handler。
看下signal和其handler的注册
Java类jdk.internal.misc.Signal
/**
* Registers a signal handler.
*/
public static synchronized Signal.Handler handle(Signal sig,
Signal.Handler handler)
throws IllegalArgumentException {
...
long newH = (handler instanceof NativeHandler) ?
((NativeHandler)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) {
Signal.Handler oldHandler = handlers.get(sig);
handlers.remove(sig);
if (newH == 2) {
handlers.put(sig, handler);
}
...
}
}
...
/* Registers a native signal handler, and returns the old handler.
* Handler values:
* 0 default handler
* 1 ignore the signal
* 2 call back to Signal.dispatch
* other arbitrary native signal handlers
*/
private static native long handle0(int sig, long nativeH);
该方法会为signal注册其Java版本的Handler。首先,它会保存下signal和其handler的映射关系,供dispatch方法使用,然后再调用handle0这个native方法,去操作系统层面进行真正的signal注册。
看下handle0方法
C文件src/java.base/share/native/libjava/Signal.c
JNIEXPORT jlong JNICALL
Java_jdk_internal_misc_Signal_handle0(JNIEnv *env, jclass cls, jint sig, jlong handler)
{
return ptr_to_jlong(JVM_RegisterSignal(sig, jlong_to_ptr(handler)));
}
再看下JVM_RegisterSignal
C++文件src/hotspot/os/posix/jvm_posix.cpp
JVM_ENTRY_NO_ENV(void*, JVM_RegisterSignal(jint sig, void* handler))
void* newHandler = handler == (void *)2
? os::user_handler()
: handler;
switch (sig) {
...
}
void* oldHandler = os::signal(sig, newHandler);
if (oldHandler == os::user_handler()) {
return (void *)2;
} else {
return oldHandler;
}
JVM_END
该方法中,如果handler参数为2的话,使用的真实handler是os::user_handler(),否则的话就是使用传入的handler。之后调用os::signal为signal注册这个处理函数。
细心的朋友可能还记得,在Signal Dispatcher线程启动的最后,也是调用了os::signal方法,为SIGBREAK (SIGQUIT) 注册处理函数,而那个处理函数,也是os::user_handler()。
接下来我们看下 os::user_handler 及其相关函数
C++文件src/hotspot/os/linux/os_linux.cpp
static void UserHandler(int sig, void *siginfo, void *context) {
...
os::signal_notify(sig);
}
void* os::user_handler() {
return CAST_FROM_FN_PTR(void*, UserHandler);
}
...
// a counter for each possible signal value
static volatile jint pending_signals[NSIG+1] = { 0 };
// Linux(POSIX) specific hand shaking semaphore.
static Semaphore* sig_sem = NULL;
...
void os::signal_notify(int sig) {
if (sig_sem != NULL) {
Atomic::inc(&pending_signals[sig]);
sig_sem->signal();
} else {
...
}
}
static int check_pending_signals() {
...
for (;;) {
for (int i = 0; i < NSIG + 1; i++) {
jint n = pending_signals[i];
if (n > 0 && n == Atomic::cmpxchg(n - 1, &pending_signals[i], n)) {
return i;
}
}
JavaThread *thread = JavaThread::current();
ThreadBlockInVM tbivm(thread);
bool threadIsSuspended;
do {
...
sig_sem->wait();
// were we externally suspended while we were waiting?
threadIsSuspended = thread->handle_special_suspend_equivalent_condition();
...
} while (threadIsSuspended);
}
}
int os::signal_wait() {
return check_pending_signals();
}
上面几个方法都是相关联的,所以放在了一起。我们还继续看os::user_handler方法。
由这个方法我们可以知道,signal的真实处理函数为UserHandler,而UserHandler的处理逻辑为调用os::signal_notify,通知signal发生。
在os::signal_notify方法里,对应的pending_signals加1,然后调用Semaphore类型变量sig_sem的signal方法,通知等待端有signal了。
而os::signal_wait就是这个等待端,它调用check_pending_signals方法,内部逻辑是检查pending_signals,如果有就返回,如果没有就调用sig_sem->wait()等待。
那谁在调用os::signal_wait呢?对,就是Signal Dispatcher这个线程,该线程启动后就开始调用os::signal_wait,等待signal,wait方法返回之后,获取这个signal,然后调用Java类jdk.internal.misc.Signal的dispatch方法,交给其进一步处理。
至此,整个signal的处理逻辑就终于形成了一个闭环。
整体逻辑我们再梳理下:
首先是启动Signal Dispatcher线程,调用os::signal_wait方法等待signal的发生,如果发生了,就调用Java类jdk.internal.misc.Signal的dispatch方法,交给其进一步处理。
之后我们可以调用Java类jdk.internal.misc.Signal的handle方法,为signal注册Java版的handler。
在这个handle方法里面,先是保存signal及其handler的映射关系,供dispatch方法使用,接着调用handle0这个native方法,进行signal的系统级注册。
handle0里为signal注册的handler都是统一的UserHandler方法。
再之后signal发生,触发UserHandler函数,该函数又会调用os::signal_notify通知Signal Dispatcher线程有新signal了。
Signal Dispatcher线程从os::signal_wait方法中返回,获取到这个signal,然后调用了Java类jdk.internal.misc.Signal的dispatch方法。
该dispatch方法找到对应的Java版的handler,开启新Java Thread执行这个handler。
整个逻辑清楚了,那我们接下来看下,都是谁调用了handle方法,注册了handler。
Java类java.lang.Terminator
class Terminator {
private static Signal.Handler handler = null;
/* Invocations of setup and teardown are already synchronized
* on the shutdown lock, so no further synchronization is needed here
*/
static void setup() {
if (handler != null) return;
Signal.Handler sh = new Signal.Handler() {
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) {
}
}
static void teardown() {
/* The current sun.misc.Signal class does not support
* the cancellation of handlers
*/
}
}
可以看到,是Terminator的setup方法为HUP、INI、TERM信号注册了handler,而handler的处理逻辑就是shutdown整个JVM。
而这个方法又是被System的initPhase1方法调用的。
Java类java.lang.System
private static void initPhase1() {
...
// Setup Java signal handlers for HUP, TERM, and INT (where available).
Terminator.setup();
...
}
最后看下Java进程的的线程堆栈,确认下Signal Dispatcher线程是真实存在的。
➜ ~ jstack 30460
2019-01-03 16:17:08
Full thread dump Java HotSpot(TM) 64-Bit Server VM (11+28 mixed mode):
...
"Signal Dispatcher" #4 daemon prio=9 os_prio=0 cpu=0.22ms elapsed=49.34s tid=0x00007f15b013b000 nid=0x7711 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
...
JNI global refs: 15, weak refs: 0
完。
更好的排版及最新的校正请点击阅读原文。
以上是关于HotSpot JVM中的signal的主要内容,如果未能解决你的问题,请参考以下文章