Java Signal实例



http://wangym.iteye.com/blog/733693

TimYang在博文《降低应用latency方法谈》 中提到对Java方法进行Profiling,在某些场景更倾向于采用纯Java工具类的方法,比如通过给进程发Signals来实现,以求动态地打开或关闭Profiler。

 

感觉这个思路不错,以下是一个大致实例:

 

SignalTest.java

 

Java代码 复制代码  收藏代码
  1. /** 
  2.  *  
  3.  */  
  4. package signal;  
  5.   
  6. import java.util.Observable;  
  7. import java.util.Observer;  
  8. import sun.misc.Signal;  
  9. import sun.misc.SignalHandler;  
  10.   
  11. /** 
  12.  * @author xuanyin 
  13.  *  
  14.  */  
  15. public class SignalTest implements Observer {  
  16.   
  17.     /** 
  18.      * @param args 
  19.      */  
  20.     public static void main(String[] args) {  
  21.   
  22.         new SignalTest().go();  
  23.     }  
  24.   
  25.     private void go() {  
  26.   
  27.         try {  
  28.   
  29.             HandlerTest sh = new HandlerTest();  
  30.             sh.addObserver(this);  
  31.             sh.handleSignal("HUP");  
  32.             sh.handleSignal("BUS");  
  33.             System.out.println("Sleeping for 60 seconds: hit me with signals!");  
  34.             Thread.sleep(60000);  
  35.   
  36.         } catch (Throwable x) {  
  37.   
  38.             x.printStackTrace();  
  39.         }  
  40.     }  
  41.   
  42.     /** 
  43.      *  
  44.      */  
  45.     @Override  
  46.     public void update(Observable arg0, Object arg1) {  
  47.   
  48.         System.out.println("Received signal: " + arg1);  
  49.     }  
  50.   
  51.     /** 
  52.      * HandlerTest Class 
  53.      */  
  54.     class HandlerTest extends Observable implements SignalHandler {  
  55.   
  56.         @Override  
  57.         public void handle(Signal signal) {  
  58.   
  59.             setChanged();  
  60.             notifyObservers(signal);  
  61.         }  
  62.   
  63.         /** 
  64.          *  
  65.          * @param signalName 
  66.          * @throws IllegalArgumentException 
  67.          */  
  68.         public void handleSignal(String signalName) throws IllegalArgumentException {  
  69.   
  70.             try {  
  71.   
  72.                 Signal.handle(new Signal(signalName), this);  
  73.   
  74.             } catch (IllegalArgumentException x) {  
  75.   
  76.                 throw x;  
  77.   
  78.             } catch (Throwable x) {  
  79.   
  80.                 throw new IllegalArgumentException("Signal unsupported: "+signalName, x);  
  81.             }  
  82.         }  
  83.     }  
  84.   
  85. }  
/**
 * 
 */
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可以去注册一个信号的处理函数,方法的如下:

  1.  public static synchronized SignalHandler handle(Signal sig,   
  2.                             SignalHandler handler) {  
  3. ....  
  4. }  
 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源码,如下面所示

  1.  public static synchronized SignalHandler handle(Signal sig,   
  2.                         SignalHandler handler)   
  3. throws IllegalArgumentException {  
  4. long newH = (handler instanceof NativeSignalHandler) ?   
  5.               ((NativeSignalHandler)handler).getHandler() : 2;  
  6. long oldH = handle0(sig.number, newH);  
  7. if (oldH == -1) {  
  8.     throw new IllegalArgumentException  
  9.     ("Signal already used by VM: " + sig);  
  10. }  
  11. signals.put(new Integer(sig.number), sig);  
  12. synchronized (handlers) {  
  13.     SignalHandler oldHandler = (SignalHandler)handlers.get(sig);  
  14.     handlers.remove(sig);  
  15.     if (newH == 2) {  
  16.         handlers.put(sig, handler);           
  17.     }  
  18.     if (oldH == 0) {  
  19.         return SignalHandler.SIG_DFL;  
  20.     } else if (oldH == 1) {  
  21.         return SignalHandler.SIG_IGN;  
  22.     } else if (oldH == 2) {  
  23.         return oldHandler;  
  24.     } else {  
  25.         return new NativeSignalHandler(oldH);  
  26.     }  
  27. }  
  28.    }  
  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,具体来看一下这个函数的实现

  1. JVM_ENTRY_NO_ENV(void*, JVM_RegisterSignal(jint sig, void* handler))  
  2.   // Copied from classic vm  
  3.   // signals_md.c       1.4 98/08/23  
  4.   void* newHandler = handler == (void *)2  
  5.                    ? os::user_handler()  
  6.                    : handler;  
  7.   switch (sig) {  
  8.     /* The following are already used by the VM. */  
  9.     case INTERRUPT_SIGNAL:  
  10.     case SIGFPE:  
  11.     case SIGILL:  
  12.     case SIGSEGV:  
  13.   
  14.     /* The following signal is used by the VM to dump thread stacks unless 
  15.        ReduceSignalUsage is set, in which case the user is allowed to set 
  16.        his own _native_ handler for this signal; thus, in either case, 
  17.        we do not allow JVM_RegisterSignal to change the handler. */  
  18.     case BREAK_SIGNAL:  
  19.       return (void *)-1;  
  20.   
  21.     /* The following signals are used for Shutdown Hooks support. However, if 
  22.        ReduceSignalUsage (-Xrs) is set, Shutdown Hooks must be invoked via 
  23.        System.exit(), Java is not allowed to use these signals, and the the 
  24.        user is allowed to set his own _native_ handler for these signals and 
  25.        invoke System.exit() as needed. Terminator.setup() is avoiding 
  26.        registration of these signals when -Xrs is present. 
  27.        - If the HUP signal is ignored (from the nohup) command, then Java 
  28.          is not allowed to use this signal. 
  29.      */  
  30.   
  31.     case SHUTDOWN1_SIGNAL:  
  32.     case SHUTDOWN2_SIGNAL:  
  33.     case SHUTDOWN3_SIGNAL:  
  34.       if (ReduceSignalUsage) return (void*)-1;  
  35.       if (os::Linux::is_sig_ignored(sig)) return (void*)1;  
  36.   }  
  37.   
  38.   void* oldHandler = os::signal(sig, newHandler);  
  39.   if (oldHandler == os::user_handler()) {  
  40.       return (void *)2;  
  41.   } else {  
  42.       return oldHandler;  
  43.   }  
  44. 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


  1. void* newHandler = handler == (void *)2  
  2.                    ? os::user_handler()  
  3.                    : 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

  1. static void signal_thread_entry(JavaThread* thread, TRAPS) {  
  2.       ....  
  3.       default: {  
  4.         // Dispatch the signal to java  
  5.         HandleMark hm(THREAD);  
  6.         klassOop k = SystemDictionary::resolve_or_null(vmSymbolHandles::sun_misc_Signal(), THREAD);  
  7.         KlassHandle klass (THREAD, k);  
  8.         if (klass.not_null()) {  
  9.           JavaValue result(T_VOID);  
  10.           JavaCallArguments args;  
  11.           args.push_int(sig);  
  12.           JavaCalls::call_static(  
  13.             &result,  
  14.             klass,  
  15.             vmSymbolHandles::dispatch_name(),  
  16.             vmSymbolHandles::int_void_signature(),  
  17.             &args,  
  18.             THREAD  
  19.           );  
  20.         }  
  21.         ....  
  22. }  
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 线程。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值