http://wangym.iteye.com/blog/733693
TimYang在博文《降低应用latency方法谈》 中提到对Java方法进行Profiling,在某些场景更倾向于采用纯Java工具类的方法,比如通过给进程发Signals来实现,以求动态地打开或关闭Profiler。
感觉这个思路不错,以下是一个大致实例:
SignalTest.java
- /**
- *
- */
- package signal;
- import java.util.Observable;
- import java.util.Observer;
- import sun.misc.Signal;
- import sun.misc.SignalHandler;
- /**
- * @author xuanyin
- *
- */
- public class SignalTest implements Observer {
- /**
- * @param args
- */
- public static void main(String[] args) {
- new SignalTest().go();
- }
- private void go() {
- try {
- HandlerTest sh = new HandlerTest();
- sh.addObserver(this);
- sh.handleSignal("HUP");
- sh.handleSignal("BUS");
- System.out.println("Sleeping for 60 seconds: hit me with signals!");
- Thread.sleep(60000);
- } catch (Throwable x) {
- x.printStackTrace();
- }
- }
- /**
- *
- */
- @Override
- public void update(Observable arg0, Object arg1) {
- System.out.println("Received signal: " + arg1);
- }
- /**
- * HandlerTest Class
- */
- class HandlerTest extends Observable implements SignalHandler {
- @Override
- public void handle(Signal signal) {
- setChanged();
- notifyObservers(signal);
- }
- /**
- *
- * @param signalName
- * @throws IllegalArgumentException
- */
- public void handleSignal(String signalName) throws IllegalArgumentException {
- try {
- Signal.handle(new Signal(signalName), this);
- } catch (IllegalArgumentException x) {
- throw x;
- } catch (Throwable x) {
- throw new IllegalArgumentException("Signal unsupported: "+signalName, x);
- }
- }
- }
- }
/**
*
*/
package signal;
import java.util.Observable;
import java.util.Observer;
import sun.misc.Signal;
import sun.misc.SignalHandler;
/**
* @author xuanyin
*
*/
public class SignalTest implements Observer {
/**
* @param args
*/
public static void main(String[] args) {
new SignalTest().go();
}
private void go() {
try {
HandlerTest sh = new HandlerTest();
sh.addObserver(this);
sh.handleSignal("HUP");
sh.handleSignal("BUS");
System.out.println("Sleeping for 60 seconds: hit me with signals!");
Thread.sleep(60000);
} catch (Throwable x) {
x.printStackTrace();
}
}
/**
*
*/
@Override
public void update(Observable arg0, Object arg1) {
System.out.println("Received signal: " + arg1);
}
/**
* HandlerTest Class
*/
class HandlerTest extends Observable implements SignalHandler {
@Override
public void handle(Signal signal) {
setChanged();
notifyObservers(signal);
}
/**
*
* @param signalName
* @throws IllegalArgumentException
*/
public void handleSignal(String signalName) throws IllegalArgumentException {
try {
Signal.handle(new Signal(signalName), this);
} catch (IllegalArgumentException x) {
throw x;
} catch (Throwable x) {
throw new IllegalArgumentException("Signal unsupported: "+signalName, x);
}
}
}
}
首先运行执行上述程序,然后查看其系统进程号。
如,若是4089,则在终端中执行kill -s BUS 4089
Java程序输出:Received signal: SIGBUS
信号具有平台相关性,不同平台下能使用的信号种类是有差异的。
Linux下支持的信号:
SEGV, ILL, FPE, BUS, SYS, CPU, FSZ, ABRT, INT, TERM, HUP, USR1, USR2, QUIT, BREAK, TRAP, PIPE
Windows下支持的信号:
SEGV, ILL, FPE, ABRT, INT, TERM, BREAK
不足之处欢迎大家留言指正:)
======================
http://blog.csdn.net/raintungli/article/details/7310141
在java 中调用Signal的方法handle可以去注册一个信号的处理函数,方法的如下:
- public static synchronized SignalHandler handle(Signal sig,
- SignalHandler handler) {
- ....
- }
public static synchronized SignalHandler handle(Signal sig,
SignalHandler handler) {
....
}
比如常用的addShutdownHook钩子函数里,就是在 Terminator.setup();的时候将Shutdown.exit 的函数注册到了信号SHUTDOWN1_SIGNAL(SIGHUP),SHUTDOWN2_SIGNAL(SIGINT),SHUTDOWN3_SIGNAL(SIGTERM)中,当线程接受到信号时,通过调用函数Shutdown.exit的调用hook中的钩子函数。
在笔者的文章(java 中关于信号的处理在linux下的实现)也提到jdk如何处理信号的,那么调用handle里是不是直接就把这个方法注册进了信号处理呢?
请注意,handle是一个java的方法,而注册信号函数是c的代码,显然不能简单的将java的方法直接提供给c调用,其次信号处理函数是在内核态中处理,安全性和执行时间的长短将影响到内核的信号调度。
先看java源码,如下面所示
- 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: " + sig);
- }
- signals.put(new Integer(sig.number), sig);
- synchronized (handlers) {
- SignalHandler oldHandler = (SignalHandler)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);
- }
- }
- }
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: " + sig);
}
signals.put(new Integer(sig.number), sig);
synchronized (handlers) {
SignalHandler oldHandler = (SignalHandler)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);
}
}
}
在native code hand0里并没有将handle的方法传进去,只是传了一个整型值。
在c++代码中hand0里调用了函数 JVM_RegisterSignal,具体来看一下这个函数的实现
- 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 INTERRUPT_SIGNAL:
- case SIGFPE:
- case SIGILL:
- case SIGSEGV:
- /* 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::Linux::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
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 INTERRUPT_SIGNAL:
case SIGFPE:
case SIGILL:
case SIGSEGV:
/* 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::Linux::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
- void* newHandler = handler == (void *)2
- ? os::user_handler()
- : handler;
void* newHandler = handler == (void *)2
? os::user_handler()
: handler;
因为传进的值是2,那么真正在c++里的信号处理函数实际上os::user_handler(),同时jvm也保护了几个信号,不允许外部改变信号的处理函数。
一切豁然开朗,笔者的博客(java 中关于信号的处理在linux下的实现)已经提到过这个函数,通过os:signal_notify 去通知signal dispatcher 线程的os::signal_wait,也就是接受到信号的线程通过信号函数notify到处理信号的线程(signal dispatcher ),最后由该线程做后续的事情。
具体来看signal dispatcher 的thread entry
- static void signal_thread_entry(JavaThread* thread, TRAPS) {
- ....
- default: {
- // Dispatch the signal to java
- HandleMark hm(THREAD);
- klassOop k = SystemDictionary::resolve_or_null(vmSymbolHandles::sun_misc_Signal(), THREAD);
- KlassHandle klass (THREAD, k);
- if (klass.not_null()) {
- JavaValue result(T_VOID);
- JavaCallArguments args;
- args.push_int(sig);
- JavaCalls::call_static(
- &result,
- klass,
- vmSymbolHandles::dispatch_name(),
- vmSymbolHandles::int_void_signature(),
- &args,
- THREAD
- );
- }
- ....
- }
static void signal_thread_entry(JavaThread* thread, TRAPS) {
....
default: {
// Dispatch the signal to java
HandleMark hm(THREAD);
klassOop k = SystemDictionary::resolve_or_null(vmSymbolHandles::sun_misc_Signal(), THREAD);
KlassHandle klass (THREAD, k);
if (klass.not_null()) {
JavaValue result(T_VOID);
JavaCallArguments args;
args.push_int(sig);
JavaCalls::call_static(
&result,
klass,
vmSymbolHandles::dispatch_name(),
vmSymbolHandles::int_void_signature(),
&args,
THREAD
);
}
....
}
也就是在jvm的c++源码中,反调用了java的方法,也就是signal.java中的dispatch(int number),方法dispatch中才是真正的调用了在文章开头提到的注册到Signal的方法handle。
dispatch方法中仍然起了一个新的线程去处理handle,这样就不会block signal dispatcher 线程。