在本机代码中处理信号-终端中出现带有SIGSEGV的JVM崩溃



这是我的第一篇文章,请大家理解。我有一些java代码和一些本地代码。

java部分目前不太有趣,所以我将跳到c++部分:

//some more trivial includes
#include <signal.h>

//these are global variables
jclass GLOBAL_CLASS;
JNIEnv * GLOBAL_ENV;
jobject GLOBAL_OBJECT;
jmethodID METHOD_ID;
void sigproc(int signo)
{
if (signo == SIGINT)
{
signal(SIGINT, sigproc);
//if Ctrl-c is pressed I want to call a method within my java class
//since I can pass only int to this function
//I've decided to use global variables
GLOBAL_ENV->CallVoidMethod(GLOBAL_OBJECT, METHOD_ID);
exit(0);
}
}
JNIEXPORT void JNICALL Java_intern_Work_readFromFile
(JNIEnv *env, jobject obj, jobjectArray arr)
{
/*define a signal trap! */
signal(SIGINT, sigproc);
//sigproc(SIGINT);
/*initialize the global variables */
GLOBAL_ENV = env;
GLOBAL_OBJECT = obj;
GLOBAL_CLASS = env->GetObjectClass(obj);
//method id is the same so it's better to cache it
//at the beginning
jmethodID mid = env->GetMethodID(GLOBAL_CLASS,
"nativeListener", 
"(Ljava/lang/String;)V");
METHOD_ID = GLOBAL_ENV->GetMethodID(GLOBAL_CLASS,
"closeEverything", "()V");
//let's say I have a while(true) block just below
//and some more work is done.
}

这个函数是在我的MainClass开始时触发的。如果我删除,程序将正确运行

GLOBAL_ENV->CallVoidMethod(GLOBAL_OBJECT, METHOD_ID);

但问题是我需要它,因为我计划释放一些动态分配的内存+我需要调用我类的这个函数。(换句话说…当我在终端中按下ctrl-c时,它会显示JVM与SIGSEGV进行切换)

当一个信号从内核传来时,我似乎真的不明白到底发生了什么。我的全局变量global_ENV仍然是我可以使用的正确指针吗?

有人能告诉我一个优雅的方法来解决我的问题吗?或者也欢迎任何指导!任何解释。。。什么都可以。提前感谢!

以下是JVM崩溃代码的示例:

A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00007f9974cfc021, pid=7099, tid=140297087112960
#
# JRE version: 6.0_24-b24
# Java VM: OpenJDK 64-Bit Server VM (20.0-b12 mixed mode linux-amd64 compressed oops)
# Derivative: IcedTea6 1.11.4
# Distribution: Ubuntu 12.04 LTS, package 6b24-1.11.4-1ubuntu0.12.04.1
# Problematic frame:
# V  [libjvm.so+0x617021]  methodOopDesc::result_type() const+0x31

您的问题是SIGINT异步信号;它可以发生在任何两个机器指令之间,除非被阻止。

这意味着从信号处理程序中调用异步安全函数以外的任何函数都是不安全的(而且,如果您希望是可移植的,则除了设置sig_atomic_t变量之外,不应该做任何事情)。JVM当然不算是异步安全的。最有可能的情况是,您正在一些重要代码的中间中断JVM,并且您的方法调用正在破坏JVM状态。

通常用于处理SIGINT的方法是在某个地方有一个检查标志变量(类型为sig_atomic_t)的循环。当您获得SIGINT时,设置标志并返回。循环将循环并以安全、同步的方式执行处理程序的其余部分。

在您的情况下,您可以生成一个Java线程,该线程定期调用一个checkForInterrupt函数来检查前面提到的标志变量。checkForInterrupt返回当前标志状态,然后线程可以选择对其进行操作

另一种选择是使用类似pausesigwaitsigsuspend的函数来挂起线程,直到接收到信号。然后线程唤醒并同步处理信号。

看看http://javajiggle.com/2008/01/06/if-jni-based-application-is-crashing-check-signal-handling/看起来这可能就是你所描述的问题。

编辑:那个链接是旧的,这是其中的信息:

http://docs.oracle.com/javase/7/docs/technotes/guides/vm/signal-chaining.html

要使用libjsig.so,请将其与创建/嵌入HotSpot虚拟机,例如:

cc-L-ljsig-ljvm java_application.c

或者使用LD_PRELOAD环境变量,例如:

export LD_PRELOAD=/libjsig.so;java_application(ksh)

setenv LD_PRELOAD/libjsig.so;java_application(csh)

插入的signal()/sigset()/sigaction()返回保存的信号处理程序,而不是Java HotSpot VM安装的信号处理程序其被OS看到。

引入信号链接设施是为了解决增强编号4381843。

最新更新