这是我的第一篇文章,所以请表现出一些理解。 我有一些 Java 代码和一些 native 代码。
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 cheshes with 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
返回当前标志状态,然后您的线程可以选择对其进行操作。
另一种选择是使用类似pause
、sigwait
或sigsuspend
的函数来挂起线程,直到收到信号。然后线程唤醒并同步处理信号。
关于java - 在 native 代码中处理信号 - 在终端中使用 SIGSEGV 导致 JVM 崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12433751/