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(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中。
下面的源码是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内核信号处理机制介绍)