4

这是我的第一篇文章,所以请表现出一些理解。我有一些 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 cheshes 与 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
4

2 回答 2

7

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

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

The approach usually used for handling SIGINT is to have a loop somewhere which checks a flag variable (of type sig_atomic_t). When you get a SIGINT, set the flag and return. The loop will come around and execute the rest of the handler in a safe, synchronous fashion.

In your case, you can just spawn off a Java thread which periodically calls a checkForInterrupt function that checks the aforementioned flag variable. checkForInterrupt returns the current flag status, and your thread can then choose to act on it.

The other option is to use a function like pause, sigwait or sigsuspend to suspend a thread until a signal is received. The thread then wakes up and processes the signal synchronously.

于 2012-09-15T03:26:20.837 回答
3

看看 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 VM 的应用程序链接,例如:

cc -L -ljsig -ljvm java_application.c

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

导出 LD_PRELOAD=/libjsig.so; java_application (ksh)

setenv LD_PRELOAD /libjsig.so; java_application (csh)

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

引入了信号链工具以解决请求增强号 4381843。

于 2012-09-15T02:14:11.447 回答