본문 바로가기
프로그래밍 놀이터/안드로이드, Java

[Java] JNI Tutorial - Invocation Interface.

by 돼지왕 왕돼지 2012. 4. 5.
반응형



안녕하세요 돼지왕왕돼지입니다.

오늘은 JNI Tutorial 중 "Invocation Interface" 에 대해 알아보겠습니다.

이 글은  http://java.sun.com/docs/books/jni/html/invoke.html#11202 내용을 요약 정리한 내용입니다.

Invocation Interface

이번 section 에서는 JVM 을 어떻게 native application 에 포함시킬지에 대해 알아봅니다. JVM 구현은 보통 native library 형태로 전달됩니다. native application 은 이 library 를 링크하고, invocation interface 를 통해 JVM 을 로드합니다.





Creating the Java Virtual Machine

<JNI C, C++>

#include <jni.h>

 

 #define PATH_SEPARATOR ';' /* define it to be ':' on Solaris */

 #define USER_CLASSPATH "." /* where Prog.class is */

 

 main() {

     JNIEnv *env;

     JavaVM *jvm;

     jint res;

     jclass cls;

     jmethodID mid;

     jstring jstr;

     jclass stringClass;

     jobjectArray args;

 

 #ifdef JNI_VERSION_1_2

     JavaVMInitArgs vm_args;

     JavaVMOption options[1];

     options[0].optionString =

         "-Djava.class.path=" USER_CLASSPATH;

     vm_args.version = 0x00010002;

     vm_args.options = options;

     vm_args.nOptions = 1;

     vm_args.ignoreUnrecognized = JNI_TRUE;

     /* Create the Java VM */

     res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);

 #else

     JDK1_1InitArgs vm_args;

     char classpath[1024];

     vm_args.version = 0x00010001;

     JNI_GetDefaultJavaVMInitArgs(&vm_args);

     /* Append USER_CLASSPATH to the default system class path */

     sprintf(classpath, "%s%c%s",

             vm_args.classpath, PATH_SEPARATOR, USER_CLASSPATH);

     vm_args.classpath = classpath;

     /* Create the Java VM */

     res = JNI_CreateJavaVM(&jvm, &env, &vm_args);

 #endif /* JNI_VERSION_1_2 */

 

     if (res < 0) {

         fprintf(stderr, "Can't create Java VM\n");

         exit(1);

     }

     cls = (*env)->FindClass(env, "Prog");

     if (cls == NULL) {

         goto destroy;

     }

 

     mid = (*env)->GetStaticMethodID(env, cls, "main",

                                     "([Ljava/lang/String;)V");

     if (mid == NULL) {

         goto destroy;

     }

     jstr = (*env)->NewStringUTF(env, " from C!");

     if (jstr == NULL) {

         goto destroy;

     }

     stringClass = (*env)->FindClass(env, "java/lang/String");

     args = (*env)->NewObjectArray(env, 1, stringClass, jstr);

     if (args == NULL) {

         goto destroy;

     }

     (*env)->CallStaticVoidMethod(env, cls, mid, args);

 

 destroy:

     if ((*env)->ExceptionOccurred(env)) {

         (*env)->ExceptionDescribe(env);

     }

     (*jvm)->DestroyJavaVM(jvm);

 }


<Java>

public class Prog {

     public static void main(String[] args) {

          System.out.println("Hello World " + args[0]);

     }

}


JVM 을 load 하기 위해서는 initialize argument 들이 필요합니다. JDK1.1에는 JDK1_1InitArgs 라는 것을 사용하고, JDK1.2 부터는 JavaVMInitArgs 라는 것을 제공합니다. ( JDK1_1InitArgs 는 1.2에서도 물론 사용 가능합니다. ) JDK 1.1 에서는 JNI_GetDefaultJavaWMInitArgs 를 호출하여 default VM setting 을 얻어옵니다. 이 함수는 heap size, stack size, default class path 등을 vm_args 에 담아서 제공합니다. 이 vm_args에호출하고자 하는 class 의 path 를 vm_args.classpath 에 저장합니다. 1.2에서는 JavaVMOption array 에 initialize array 에 저장합니다. 그리고 command line 에 들어갈, common option이나 implementation specific option 들을 넣어줍니다. ignoreUnrecognized 필드를 JNI_TRUE 로 설정함으로서 인식 못하는 option 들은 ignore 할 수 있게 해줄 수 있습니다. 


이렇게 init args 설정을 마치면, JNI_CreateJavaVM 을 호출하여 JVM 을 load 하고 initialize 해줍니다. 이 함수는 JVM 에 대한 interface pointer 와 JNIEnv interface pointer 를 return 해줍니다. 


사용을 마치면 DestoryJavaVM 함수를 호출하여 JVM 을 unload 합니다. ( JDK 1.1과 JDK 1.2 에서는 DestoryJavaVM 이 항상 에러 코드를 return 합니다. ).





Linking Native Applications with the Java Virtual Machine.

Linking with a known Java Virtual Machine.


@Solaris

cc -I<jni.h dir> -L<libjava.so dir> -lthread -ljava invoke.c


-lthread 는 native thread support 하에 JVM 을 사용하겠다는 의미이고, -ljava 는 libjava.so 는 Solaris version lib 이라는 걸 명시합니다.

@Windows

cl -I<jni.h dir> -MD invoke.c -link <javai.lib dir>\javai.lib


-MD 는 Win32 multithreaded C library 와 native app 이 연결되었다는 것을 의미하고, javai.lib 파일은 VM 연결과 관련된 내용을 담고 있습니다. windows 버전에서는 javai.dll 이라는 dynamic link library 를 runtime 이 가지고 있습니다.

JDK 1.2 version 에서는 libvm.so 와 jvm.lib 또는 jvm.dll 이 각각 사용됩니다.

shared library 나 dynmaic link library 연결에 실패한다면, LD_LIBRARY_PATH 나 PATH variable 에 path 를 제대로 기술해줘야 합니다.


Linking with Unknown Java Virtual Machines.


다른 알려지지 않은 vendor 의 VM 을 쓰기 위해서는 

@Windows
LoadLibrary와 GetProcAddress 를 통해서 코드에서 load 를 해주어야 합니다.

/* Win32 version */

 void *JNU_FindCreateJavaVM(char *vmlibpath)

 {

     HINSTANCE hVM = LoadLibrary(vmlibpath);

     if (hVM == NULL) {

         return NULL;

     }

     return GetProcAddress(hVM, "JNI_CreateJavaVM");

 }



@Solaris
dlopen 과 dlsym 을 통해 libary 를 로드해줘야 합니다.

/* Solaris version */

 void *JNU_FindCreateJavaVM(char *vmlibpath)

 {

     void *libVM = dlopen(vmlibpath, RTLD_LAZY);

     if (libVM == NULL) {

         return NULL;

     }

     return dlsym(libVM, "JNI_CreateJavaVM");

 }






Attaching Native Threads

어떤 일을 처리할 때 ( 예를 들면 네트워크 ), 여러개의 thread 로 나눠서 일을 처리할 일이 많이 생깁니다. 이 때 여러 thread 에서 JVM 을 사용하게 될 경우에는 다른 thread 를 방해하지 않고, JVM 을 쓰려고 하는 thread 가 JVM 을 붙였다가, 떼었다가 할 수 있어야 합니다. JVM 보다 해당 thread 가 먼저 종료될 수 있기 때문입니다.

다음 코드는 windows 에서 JVM에 thread 를 붙이고 떼는 것을 보여줍니다.

/* Note: This program only works on Win32 */

 #include <windows.h>

 #include <jni.h>

 JavaVM *jvm; /* The virtual machine instance */

 

 #define PATH_SEPARATOR ';'

 #define USER_CLASSPATH "." /* where Prog.class is */

 

 void thread_fun(void *arg)

 {

     jint res;

     jclass cls;

     jmethodID mid;

     jstring jstr;

     jclass stringClass;

     jobjectArray args;

     JNIEnv *env;

     char buf[100];

     int threadNum = (int)arg;

     /* Pass NULL as the third argument */

 #ifdef JNI_VERSION_1_2

     res = (*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL);

 #else

     res = (*jvm)->AttachCurrentThread(jvm, &env, NULL);

 #endif

     if (res < 0) {

        fprintf(stderr, "Attach failed\n");

        return;

     }

     cls = (*env)->FindClass(env, "Prog");

     if (cls == NULL) {

         goto detach;

     }

     mid = (*env)->GetStaticMethodID(env, cls, "main", 

                                     "([Ljava/lang/String;)V");

     if (mid == NULL) {

         goto detach;

     }

     sprintf(buf, " from Thread %d", threadNum);

     jstr = (*env)->NewStringUTF(env, buf);

     if (jstr == NULL) {

         goto detach;

     }

     stringClass = (*env)->FindClass(env, "java/lang/String");

     args = (*env)->NewObjectArray(env, 1, stringClass, jstr);

     if (args == NULL) {

         goto detach;

     }

     (*env)->CallStaticVoidMethod(env, cls, mid, args);

 

  detach:

     if ((*env)->ExceptionOccurred(env)) {

         (*env)->ExceptionDescribe(env);

     }

     (*jvm)->DetachCurrentThread(jvm);

 }

 

 main() {

     JNIEnv *env;

     int i;

     jint res;

 

 #ifdef JNI_VERSION_1_2

     JavaVMInitArgs vm_args;

     JavaVMOption options[1];

     options[0].optionString =

         "-Djava.class.path=" USER_CLASSPATH;

     vm_args.version = 0x00010002;

     vm_args.options = options;

     vm_args.nOptions = 1;

     vm_args.ignoreUnrecognized = TRUE;

     /* Create the Java VM */

     res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);

 #else

     JDK1_1InitArgs vm_args;

     char classpath[1024];

     vm_args.version = 0x00010001;

     JNI_GetDefaultJavaVMInitArgs(&vm_args);

     /* Append USER_CLASSPATH to the default system class path */

     sprintf(classpath, "%s%c%s",

             vm_args.classpath, PATH_SEPARATOR, USER_CLASSPATH);

     vm_args.classpath = classpath;

     /* Create the Java VM */

     res = JNI_CreateJavaVM(&jvm, &env, &vm_args);

 #endif /* JNI_VERSION_1_2 */

 

 if (res < 0) {

         fprintf(stderr, "Can't create Java VM\n");

         exit(1);

     }

     for (i = 0; i < 5; i++)

         /* We pass the thread number to every thread */

         _beginthread(thread_fun, 0, (void *)i);

     Sleep(1000); /* wait for threads to start */

     (*jvm)->DestroyJavaVM(jvm);

 }


주의깊게 봐야할 것은 DestoryJavaVM 은 모든 thread 가 종료된 이후에 수행됩니다. ( JVM 이 완전히 free 되면 ) JNI_AttachCurrentThread 는 현재 thread 를 JVM 에 attach 합니다. 해당 thread 가 종료할 때에는 DetachCurrentThread 를 수행시켜 local reference 들을 해제해줍니다. 



도움이 되셨다면 손가락 꾸욱~

 



반응형

댓글