【问题标题】:interface java with C timer library using JNI使用 JNI 将 java 与 C 计时器库接口
【发布时间】:2012-07-23 16:12:37
【问题描述】:

我正在尝试使这个简单的示例工作:

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。示例应该每秒打印 tick

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
#
...

为什么?

【问题讨论】:

    标签: java java-native-interface


    【解决方案1】:

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

    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);
    }
    

    最后,我们让它可运行:

    qrtt1@qrtt1-VirtualBox:/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.
        }
    }
    

    【讨论】:

    • 不客气。但是,我查看了您的问题,没有人得到您接受的答案。如果您觉得有用,请将答案标记为已接受。
    猜你喜欢
    • 1970-01-01
    • 2020-03-01
    • 1970-01-01
    • 2013-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多