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が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非同期信号であることです。ブロックされていない限り、任意の 2 つのマシン命令の間で発生する可能性があります。

これは、シグナル ハンドラからasync-safe 関数以外を呼び出すのは安全ではないことを意味します (また、移植可能にしたい場合は、sig_atomic_t変数を設定する以外は何もしないでください)。JVM は、非同期セーフとは見なされません。ほとんどの場合、重要なコードの途中で JVM を中断しており、メソッド呼び出しによって JVM の状態が壊れています。

処理に通常使用されるアプローチは、SIGINT(タイプの) フラグ変数をチェックするループをどこかに持つことsig_atomic_tです。を取得したらSIGINT、フラグを設定して戻ります。ループが発生し、ハンドラーの残りの部分が安全な同期方式で実行されます。

あなたの場合、checkForInterrupt前述のフラグ変数をチェックする関数を定期的に呼び出す Java スレッドを生成するだけです。checkForInterruptは現在のフラグ ステータスを返し、スレッドはそれに基づいて動作することを選択できます。

pauseもう 1 つのオプションは、のような関数を使用するsigwaitsigsuspend、シグナルを受信するまでスレッドを一時停止することです。その後、スレッドが起動し、シグナルを同期的に処理します。

于 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() は、OS から見える Java HotSpot VM によってインストールされたシグナル ハンドラではなく、保存されたシグナル ハンドラを返します。

シグナル チェーン機能は、Request for Enhancement 番号 4381843 に対処するために導入されました。

于 2012-09-15T02:14:11.447 に答える