java 中关于信号的处理在linux下的实现

linux 的线程和信号

基于 NPTL 的线程库,多线程应用中的每个线程有自己独特的线程 ID,并共享同一个进程ID。应用程序可以通过调用 kill(getpid(),signo) 将信号发送到进程,如果进程中当前正在执行的线程没有阻碍此信号,则会被中断,线号处理函数会在此线程的上下文背景中执行。应用程序也可以通过调用 pthread_kill(pthread_t thread, int sig)将信号发送给指定的线程,则线号处理函数会在此指定线程的上下文背景中执行。

java里信号掩码的集合

unblocked_sigs

SIGILL

SIGSEGV

SIGBUS

SIGFPE

SR_signum

SHUTDOWN1_SIGNAL(SIGHUP)

SHUTDOWN2_SIGNAL(SIGINT)

SHUTDOWN3_SIGNAL(SIGTERM)

vm_sigs

BREAK_SIGNAL (SIGQUIT)

allowdebug_blocked_sigs

SHUTDOWN1_SIGNAL(SIGHUP)

SHUTDOWN2_SIGNAL(SIGINT)

SHUTDOWN3_SIGNAL(SIGTERM)

SIG_BLOCK

NULL

 

在多线程的应用中,每个线程可以通过调用pthread_signmask()设置本线程的信号掩码,可以设置阻塞的信号,但信号SIGKILL/SIGSTOP是不能被设置成阻塞的。

在java中,每个线程都设置了在表格中的信号掩码,特别提到的是vm_sigs,这是只有一个quit 的信号结合,当没有设置启动参数 -Xrs (=ReduceSignalUsage)的时候,其他的线程设置成阻塞的,除了vm thread。

void os::Linux::hotspot_sigmask(Thread* thread) {

  //Save caller's signal mask before setting VM signal mask
  sigset_t caller_sigmask;
  pthread_sigmask(SIG_BLOCK, NULL, &caller_sigmask);

  OSThread* osthread = thread->osthread();
  osthread->set_caller_sigmask(caller_sigmask);

  pthread_sigmask(SIG_UNBLOCK, os::Linux::unblocked_signals(), NULL);

  if (!ReduceSignalUsage) {
    if (thread->is_VM_thread()) {
      // Only the VM thread handles BREAK_SIGNAL ...
      pthread_sigmask(SIG_UNBLOCK, vm_signals(), NULL);
    } else {
      // ... all other threads block BREAK_SIGNAL
      pthread_sigmask(SIG_BLOCK, vm_signals(), NULL);
    }
  }
}

 

Java里信号处理的函数

在linux里可以设置进程级别的信号的处理函数,在内核中信号值及进程针对该信号的处理函数建立了映射关系,主要有2个函数来设置信号处理函数:signal(),sigaction()。函数具体可以参考http://www.ibm.com/developerworks/cn/linux/l-ipc/part2/index1.html

在java 里,下面的2个函数是用于设置信号处理的函数

void os::Linux::set_signal_handler(int sig, bool set_installed)

void* os:signal(int signal_number, void* handler)

  • 函数set_signal_handler设置了信号的处理函数signalHandler
  • 函数os:signal是允许外部直接设置信号处理函数
  1. 以下程序片段可以看到当每个java 线程起来的时候,对信号SIGSEGV ,SIGPIPE,SIGBUS,SIGILL,SIGFPE,SIGXGSZ,使用了专门的处理函数signalHandler
       ...
        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);
        set_signal_handler(SIGXFSZ, true);
         ......
    在signalhandler 调用了JVM_handler_linux_signal 而该函数在不同的架构下是不一样的,x86架构下的定义在os_linux_x86.cpp中。

  2. 对信号SIGQUIT源码中并没有看到处理函数,实际上当java虚拟机启动Signal Dispatcher 线程的时候,程序里调用了os::signal(SIGNBREAK,os::user_handler()) (os.cpp os::signal_init())(该线程在打开 -Xrs的时候是不启动的)

            下面的源码是java里面定义的os:user_handler

  static void
UserHandler(int sig, void *siginfo, void *context) {
  // 4511530 - sem_post is serialized and handled by the manager thread. When
  // the program is interrupted by Ctrl-C, SIGINT is sent to every thread. We
  // don't want to flood the manager thread with sem_post requests.
  if (sig == SIGINT && Atomic::add(1, &sigint_count) > 1)
      return;

  // Ctrl-C is pressed during error reporting, likely because the error
  // handler fails to abort. Let VM die immediately.
  if (sig == SIGINT && is_error_reported()) {
     os::die();
  }

  os::signal_notify(sig);
}

在函数的末尾调用了os:signal_notify,从而和os::signal_wait 对应起来,关于signal dispatcher 的具体实现可以参考笔者的另一篇博客(http://blog.csdn.net/raintungli/article/details/7034005)。

signal dispatcher 线程通过sem_wait会在等待,当进程接到信号SIGQUIT的时候,只有vm thread会被中断(见上面分析),而进入UserHandler 函数,通过调用 os::signal_notify 去通告signal dispatcher 线程,让 signal dispatch 线程去处理信号。

在信号设计里,因为信号中断是在内核态调用的,内核调用了线程注入了自己的信号函数,一般只允许在该函数里处理简单的事物,所以在java里面专门设计了处理信号后续的线程(signal dispatcher),接受到信号的线程通过信号函数notify到处理信号的线程(signal dispatcher ),最后由该线程做后续的事情。比如线程dump

关于信号中断的内核态和用户态的如何嵌入,可以参考(Linux内核信号处理机制介绍

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值