java linux 信号_Java开源工具在linux上的源码分析(二):信号处理

当java虚拟机启动的时候,会启动很多内部的线程,这些线程主要在thread.cpp里的create_vm方法体里实现。

而在thread.cpp里主要起了2个线程来处理信号相关的:

JvmtiExport::enter_live_phase();

// Signal Dispatcher needs to be started before VMInit event is posted

os::signal_init();

// Start Attach Listener if +StartAttachListener or it can't be started lazily

if(!DisableAttachMechanism) {

if(StartAttachListener || AttachListener::init_at_startup()) {

AttachListener::init();

}

}

1. Signal Dispatcher 线程

在os.cpp中的signal_init()函数中,启动了signal dispatcher 线程,对signal dispather 线程主要是用于处理信号,等待信号并且分发处理,可以详细看signal_thread_entry的方法:

staticvoidsignal_thread_entry(JavaThread* thread, TRAPS) {

os::set_priority(thread, NearMaxPriority);

while(true) {

intsig;

{

// FIXME : Currently we have not decieded what should be the status

//         for this java thread blocked here. Once we decide about

//         that we should fix this.

sig = os::signal_wait();

}

if(sig == os::sigexitnum_pd()) {

// Terminate the signal thread

return;

}

switch(sig) {

caseSIGBREAK: {

// Check if the signal is a trigger to start the Attach Listener - in that

// case don't print stack traces.

if(!DisableAttachMechanism && AttachListener::is_init_trigger()) {

continue;

}

// Print stack traces

// Any SIGBREAK operations added here should make sure to flush

// the output stream (e.g. tty->flush()) after output.  See 4803766.

// Each module also prints an extra carriage return after its output.

VM_PrintThreads op;

VMThread::execute(&op);

VM_PrintJNI jni_op;

VMThread::execute(&jni_op);

VM_FindDeadlocks op1(tty);

VMThread::execute(&op1);

Universe::print_heap_at_SIGBREAK();

if(PrintClassHistogram) {

VM_GC_HeapInspection op1(gclog_or_tty,true/* force full GC before heap inspection */,

true/* need_prologue */);

VMThread::execute(&op1);

}

if(JvmtiExport::should_post_data_dump()) {

JvmtiExport::post_data_dump();

}

break;

}

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

);

}

if(HAS_PENDING_EXCEPTION) {

// tty is initialized early so we don't expect it to be null, but

// if it is we can't risk doing an initialization that might

// trigger additional out-of-memory conditions

if(tty != NULL) {

charklass_name[256];

chartmp_sig_name[16];

constchar* sig_name ="UNKNOWN";

instanceKlass::cast(PENDING_EXCEPTION->klass())->

name()->as_klass_external_name(klass_name,256);

if(os::exception_name(sig, tmp_sig_name,16) != NULL)

sig_name = tmp_sig_name;

warning("Exception %s occurred dispatching signal %s to handler"

"- the VM may need to be forcibly terminated",

klass_name, sig_name );

}

CLEAR_PENDING_EXCEPTION;

}

}

}

}

}

可以看到通过os::signal_wait();等待信号,而在linux里是通过sem_wait()来实现,接受到SIGBREAK(linux 中的QUIT)信号的时候(关于信号处理请参考笔者的另一篇博客:java 中关于信号的处理在linux下的实现),***次通过调用 AttachListener::is_init_trigger()初始化attach listener线程,详细见2.Attach Listener 线程。

***次收到信号,会开始初始化,当初始化成功,将会直接返回,而且不返回任何线程stack的信息(通过socket file的操作返回),并且第二次将不在需要初始化。如果初始化不成功,将直接在控制台的outputstream中打印线程栈信息。

第二次收到信号,如果已经初始化过,将直接在控制台中打印线程的栈信息。如果没有初始化,继续初始化,走和***次相同的流程。

2. Attach Listener 线程

Attach Listener 线程是负责接收到外部的命令,而对该命令进行执行的并且吧结果返回给发送者。在jvm启动的时候,如果没有指定+StartAttachListener,该线程是不会启动的,刚才我们讨论到了在接受到quit信号之后,会调用 AttachListener::is_init_trigger()通过调用用AttachListener::init()启动了Attach Listener 线程,同时在不同的操作系统下初始化,在linux中 是在attachListener_Linux.cpp文件中实现的。

在linux中如果发现文件.attach_pid#pid存在,才会启动attach listener线程,同时初始化了socket 文件,也就是通常jmap,jstack tool干的事情,先创立attach_pid#pid文件,然后发quit信号,通过这种方式暗式的启动了Attach Listener线程(见博客:http://blog.csdn.net/raintungli/article/details/7023092)。

线程的实现在 attach_listener_thread_entry 方法体中实现:

staticvoidattach_listener_thread_entry(JavaThread* thread, TRAPS) {

os::set_priority(thread, NearMaxPriority);

if(AttachListener::pd_init() !=0) {

return;

}

AttachListener::set_initialized();

for(;;) {

AttachOperation* op = AttachListener::dequeue();

if(op == NULL) {

return;// dequeue failed or shutdown

}

ResourceMark rm;

bufferedStream st;

jint res = JNI_OK;

// handle special detachall operation

if(strcmp(op->name(), AttachOperation::detachall_operation_name()) ==0) {

AttachListener::detachall();

}else{

// find the function to dispatch too

AttachOperationFunctionInfo* info = NULL;

for(inti=0; funcs[i].name != NULL; i++) {

constchar* name = funcs[i].name;

assert(strlen(name) <= AttachOperation::name_length_max,"operation <= name_length_max");

if(strcmp(op->name(), name) ==0) {

info = &(funcs[i]);

break;

}

}

// check for platform dependent attach operation

if(info == NULL) {

info = AttachListener::pd_find_operation(op->name());

}

if(info != NULL) {

// dispatch to the function that implements this operation

res = (info->func)(op, &st);

}else{

st.print("Operation %s not recognized!", op->name());

res = JNI_ERR;

}

}

// operation complete - send result and output to client

op->complete(res, &st);

}

}

在AttachListener::dequeue(); 在liunx里的实现就是监听刚才创建的socket的文件,如果有请求进来,找到请求对应的操作,调用操作得到结果并把结果写到这个socket的文件,如果你把socket的文件删除,jstack/jmap会出现错误信息 unable to open socket file:........

我们经常使用 kill -3 pid的操作打印出线程栈信息,我们可以看到具体的实现是在Signal Dispatcher 线程中完成的,因为kill -3 pid 并不会创建.attach_pid#pid文件,所以一直初始化不成功,从而线程的栈信息被打印到控制台中。

【系列文章】

【责任编辑:小林 TEL:(010)68476606】

点赞 0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值