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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值