处理本机代码中的信号 - 在终端中使用SIGSEGV导致JVM崩溃

noo*_*bed 4 java linux java-native-interface signals native

这是我的第一篇文章所以请表示理解.我有一些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.
}
Run Code Online (Sandbox Code Playgroud)

在我的MainClass开始时触发此函数.如果我删除,程序运行正常

GLOBAL_ENV->CallVoidMethod(GLOBAL_OBJECT, METHOD_ID);
Run Code Online (Sandbox Code Playgroud)

但问题是我需要它,因为我打算释放一些动态分配的内存+我需要调用我的类的这个函数.(换句话说......当我在终端按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
Run Code Online (Sandbox Code Playgroud)

nne*_*neo 6

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

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

通常用于处理的方法SIGINT是在某处检查标志变量(类型sig_atomic_t)的循环.当你得到一个SIGINT,设置标志并返回.循环将以安全,同步的方式出现并执行处理程序的其余部分.

在您的情况下,您可以生成Java线程,该线程定期调用checkForInterrupt检查上述标志变量的函数.checkForInterrupt返回当前标志状态,然后您的线程可以选择对其执行操作.

另一种选择是使用类似的函数pause,sigwait或者sigsuspend在收到信号之前暂停线程.然后线程唤醒并同步处理信号.