2012-07-23 40 views
3

我试图使这个简单的例子作C类定时器库:接口的Java使用JNI

import java.io.*; 

public class Timer { 
    public static void main(String[] args) { 
     setTimer(new Runnable() { 
       public void run() { 
        System.out.println("tick"); 
       } 
      }, 
      1000 /* msecs */ 
     ); 
     while (true) ; 
    } 

    native public static void setTimer(Runnable r, int msecs); 

    static { 
     System.loadLibrary("timer"); 
    } 
} 

代码很简单。在使用SIGALRMsetitimer的JNI/C/Linux中使用程序setTimer。示例应该每秒打印刻度

JNI库被编程为:

#include <signal.h> 
#include <sys/time.h> 

#include "Timer.h" 
#include "error.h" 

static JNIEnv *genv; 
static jobject gobj; 

void handler(int s) { 
    jclass cls = (*genv)->GetObjectClass(genv, gobj); 
    jmethodID mid = (*genv)->GetMethodID(genv, cls, "run", "()V"); 
    (*genv)->CallVoidMethod(genv, gobj, mid); 
} 

JNIEXPORT void JNICALL Java_Timer_setTimer 
    (JNIEnv *env, jclass cls, jobject obj, jint msecs) { 
    struct sigaction sa; 
    struct itimerval it; 

    sa.sa_flags = SA_RESTART; 
    sigemptyset (&sa.sa_mask); 

    genv = env; 
    gobj = obj; 
    sa.sa_handler = handler; 
    it.it_interval.tv_sec = msecs/1000; 
    it.it_interval.tv_usec = (msecs%1000)*1000; 
    it.it_value = it.it_interval; 

    if (sigaction (SIGALRM/*PROF*/, &sa, NULL) == -1 
     || setitimer (ITIMER_REAL/*PROF*/, &it, NULL) == -1) 
     SysError("setTimer: sigaction, setitimer: couldn't init timer"); 
} 

我使用的信号链接,以免与JVM的信号干扰库:

LD_LIBRARY_PATH=. LD_PRELOAD=/usr/lib/jvm/java-6-openjdk/jre/lib/i386/libjsig.so java Timer 

第一次SIGALRM火灾JMV段错误有消息:

# 
# A fatal error has been detected by the Java Runtime Environment: 
# 
# SIGSEGV (0xb) at pc=0x00e9531c, pid=5742, tid=3077924544 
# 
... 

为什么?

回答

7

问题是无效genv和gobj。 genv和gobj在变量集合和Java_Timer_setTimer函数之间有效。您可以从当前的jvm获取env *并将obj设置为全局参考。我定义了新的变量:

static JavaVM *jvm = NULL; 
static jobject callback = NULL; 

在Java_Timer_setTimer:

/* find the current jvm */ 
(*env)->GetJavaVM(env, &jvm); 
/* upgrade callback to global ref */ 
callback = (*env)->NewGlobalRef(env, obj); 

在手柄,使用JVM附加当前线程调用回调函数:

void handler(int s) { 
    if(jvm == NULL) 
     return ; 
    if(callback == NULL) 
     return ; 

    JNIEnv *env = NULL; 
    jint res; 
    res = (*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL); 
    if(res < 0) 
    { 
     fprintf(stderr, "Attach VM Thread failed\n"); 
     return ; 
    } 

    jclass cls = (*env)->GetObjectClass(env, callback); 
    jmethodID mid = (*env)->GetMethodID(env, cls, "run", "()V"); 
    (*env)->CallVoidMethod(env, callback, mid); 
    (*jvm)->DetachCurrentThread(jvm); 
} 

最后,我们使其可以运行:

[email protected]:/media/sf_VBoxSharedFolder/0Lab$ java -Djava.library.path=. -classpath bin Timer 
tick Tue Jul 24 13:01:54 CST 2012 
tick Tue Jul 24 13:01:55 CST 2012 
tick Tue Jul 24 13:01:56 CST 2012 
tick Tue Jul 24 13:01:57 CST 2012 
tick Tue Jul 24 13:01:58 CST 2012 

完成的源代码是:

#include "Timer.h" 
#include <jni.h> 
#include <signal.h> 
#include <sys/time.h> 
#include <stdio.h> 


static JavaVM *jvm = NULL; 
static jobject callback = NULL; 

void handler(int s) { 
    if(jvm == NULL) 
     return ; 
    if(callback == NULL) 
     return ; 

    JNIEnv *env = NULL; 
    jint res; 
    res = (*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL); 
    if(res < 0) 
    { 
     fprintf(stderr, "Attach VM Thread failed\n"); 
     return ; 
    } 

    jclass cls = (*env)->GetObjectClass(env, callback); 
    jmethodID mid = (*env)->GetMethodID(env, cls, "run", "()V"); 
    (*env)->CallVoidMethod(env, callback, mid); 
    (*jvm)->DetachCurrentThread(jvm); 
} 

/* 
* Class:  Timer 
* Method: setTimer 
* Signature: (Ljava/lang/Runnable;I)V 
*/ 
JNIEXPORT void JNICALL Java_Timer_setTimer 
    (JNIEnv *env, jclass cls, jobject obj, jint msecs) 
{ 

    struct sigaction sa; 
    struct itimerval it; 

    sa.sa_flags = SA_RESTART; 
    sigemptyset (&sa.sa_mask); 

    /* find the current jvm */ 
    (*env)->GetJavaVM(env, &jvm); 
    /* upgrade callback to global ref */ 
    callback = (*env)->NewGlobalRef(env, obj); 

    sa.sa_handler = handler; 
    it.it_interval.tv_sec = msecs/1000; 
    it.it_interval.tv_usec = (msecs%1000)*1000; 
    it.it_value = it.it_interval; 

    if (sigaction (SIGALRM/*PROF*/, &sa, NULL) == -1 
      || setitimer (ITIMER_REAL/*PROF*/, &it, NULL) == -1) 
    { 
     // error. 
    } 
} 
+0

感谢小费。有用! – francesc 2012-07-27 17:51:46

+0

不客气。但是,我审查你的问题,没有人得到你接受的答案。如果您觉得它有用,请将答案标记为已接受。 – qrtt1 2012-07-28 01:58:56