본문 바로가기

카테고리 없음

윈도우, 리눅스에서 빠르게 JNI 예제 수행하기

728x90

[문서]

1차 수정 :2015-08-16: [참고]항목 넣음

2차 수정 :2015-08-16: Linux 에서  JNI수행하는 방법 추가.

3차 수정 :2015-08-16: [참고]->[이슈] 수정


[개념]

JNI 는 Java Native Interface의 약자로 Java 에서 다른 언어의 코드의 수행을 돕는 interface 이다.

이를 통해 특히 c/c++와 연동하여 빠른 수행을 적용할 수 있다. 그러나 보안의 문제와 이식의 문제를 야기시킴.

(안드로이드에서는 ndk 를 이용)




[수행 목표]

Android에서 JNI 수행 실패

-> Window 64bit 에서 JNI 수행 

-> Linux 32bit 에서 JNI 수행 


[수행환경]

-윈도우

intel 64bit cpu

window 7

mingw 32  ( 말미에 mingw 64 설치) : dll 생성 및 compile을 위해 필요. (dll 생성 : dlltool.exe ,   compile : mingw64)

cygwin 64 

jdk 1.8.0.51 설치 (2015-08-16)


-리눅스

CentOS 6.5 32bit

(gcc는 당연히 있음)

jdk 1.8.0.51 설치(2015-08-16)




[일지]

2015-08-14: JNI 무작정 수행 (android에서)   javah로 만드는 .h 파일 생성 실패 (원인 : compile을 못함(class 파일생성안하고 수행)

-> 컴파일이 안되는데 어떡함. 결국 해결 못함   수 번의 삽질끝에 포기.


2015-08-16: Window에서 JNI 수행하기로 마음먹음 계속된 실패 (원인 : java에서 path 설정을 못함.(JAVA_HOME을 이용한 옵션 주는 방법 잘 못함.) 

-> 해결방법 : 절대경로로 잡음. (이때서야 javac javah gcc 기타 등등 의 path가 중요하다는걸 깨달음)


2015-08-16: compile 에러 발견 (library 에러) -> 순서(*1)에 맞게 재 컴파일 후 사라짐

2015-08-16: compile 성공후 실행에서 에러 (64 bit 에러) -> gcc 가 64비트용이 아니라 32비트였음

2015-08-16: 64bit gcc 를 위해 mingw64 설치 후 64bit compile용 prompt 로 이전 gcc의 compile수행 후 예제 수행 성공.


초기 필요 파일 : HelloNative.java, jni.h , jni_md.h   (+ HelloNative.c)

주의 : 파일 생성 중에 만들어지는 파일명은 알아서 정하도록.


-HelloNative.java

class HelloNative

{

native public static void hello();

static

{

System.loadLibrary("HelloNative");

}

}

class HelloNativeTest

{

public static void main(String[] args)

{

HelloNative.hello();

}

}



- HelloNative.c  ($javah HelloNative수행 후 .c 작성하면 된다.)

#include <stdio.h>

#include "HelloNative.h" // 이놈은 위의 .java 완성후 $javah HelloNative 하면 만들어지는 파일.


JNIEXPORT void JNICALL Java_HelloNative_hello(JNIEnv *env, jclass cls)

{

  printf("This JNI Hello!\n");

}



[수행 요약]

전제 1 : 현재 수행과정 위치는 c:\dev

전제 2 : jni.h  와 jni_md.h 를 내가 수행하고자하는 폴더에 넣는다.

ex)   jni.h(from java/jdk(약식)/include 폴더에 있음)와 jni_md.h (from java/jdk(약식)/include/win32) 를 가져와서 c:\dev\에 넣어 놓음.


관련된 참고 예제 : [Mark Allen Weiss] JNI:JAVA와 C  의 연동.pdf

주의 1 : 각 compiler의 절대 위치 알기!!!!!!!!!

jdk 절대 위치(java, javac, javah 수행을 위해) , mingw 절대 위치(gcc, dlltool 수행을 위해) 반드시 알아야함.

주의 2 : 예제는 위의 참고 예제!!!!!!

주의 3 : native 단의 메소드의 수행 환경이 어디인지 알기 (64비트 인지... 32비트인지)


권장 1 : 동적 라이브러리 개념 알고 수행하길 바람


생성 순서 : .java -> .class -> .h -> .c -> .o ->(.def ->( .a +  이전의 .o )->) .dll 


다시한번 말하지만 수행 위치는 c:\dev 임

1단계 :  java 코드 작성. 

(.java 생성)


2단계 :  HelloNative.java 파일 compile 

(명령어 javac 수행) -> .class 파일 생성

$javac HelloNative.java


3단계 :  HelloNative.h 파일 생성

 (명령어 javah 로 수행, class 파일 존재해야함)

$javah HelloNative


4단계 :  .c 파일 작성 

(native 단의 메소드 구현을 여기서 수행, 위의 .h 파일 include하는 .c 파일임)

$vi HelloNative.c  (위처럼) 작성

주의 : parameter에 변수명 꼭 넣자.


5단계 :  .o 파일 생성

(해당 .c 파일 compile 수행 결과, 64bit 윈도우 라면 -m64 옵션 줘야함. )

$gcc -c HelloNative.c -o HelloNative.o -Ic:\dev -Ic:\dev -m64

-I(대문자 i)  옵션 : jni.h , jni_md.h 위치를 말해줌.


6단계 : 동적라이브러리 파일 생성 


-윈도우(6단계)

(.o 파일에 대한 동적라이브러리 파일로 linux는 .so  윈도우는 .dll)

6단계 요약 (yyy.o 파일 -> .dll 관련된 .def 파일 생성 -> 라이브러리 .a  파일 생성 -> .dll 파일 생성)

6-1단계 : $dlltool --output-def xxx.def --kill-at --dllname bbb.dll yyy.o     // .def 생성

6-2단계 : $dlltool --output-lib libXYZ.a --input-def xxx.def --kill-at --dllname bbb.dll yyy.o

6-3단계 : $gcc yyy.o -o bbb.dll -m64 -Wall -L -IXYZ -shared   (여기서 -I 는 대문자 i 그리고 libXYZ.a 에서 lib 생략하는건 약속. 자동으로 lib 읽음)

최종 bbb.dll 생성


-리눅스(6단계)

$gcc -shared -o libXYZ.so yyy.o  로 끝

주의 : 

여기서 LD_LIBRARY_PATH 설정을 해줘야함.

이 설정은 /etc/profile 맨 밑에서 적용할 수 있음.

적용하지 않으면 java.lang.UnsatiedLinkError: no XX in java.library.path 에러 뜸


해결 방법 :

/etc/profile 을 수정.(여기서 마지막 라인에 export LD_LIBRARY_PATH =./   넣거나

export LD_LIBRARY_PATH = 현재 내가 쓰려는 .so 가 있는 폴더 경로를 써주면 됌)
최종 bbb.so 생성



7단계 :java실행

(명령어 java 로 수행)



#?추측컨데 리눅스 환경에서 하려면 5 단계 까지는 유사하고 6단계에서 .so 파일만 생성하면 다 될 듯?

#?안드로이드의 경우 .apk 로 존재하는데 어떻게 .so 와  .class 가 작동하지?

#! gcc -fPIC 하면 include 하는 위치의 설정이 바뀜... ( stdio.h 에러가 나기도함. 옵션의 정확한 기능은 검색 요망)

#? JVM 에서 .dll 또는 .so 가 어떻게 실행되는지... native 함수의 경우에도 어떻게 수행되는지 그 로직을 알아내자.

#? Java 말고 android 에선 어떻게 해야하지? 그리고 path 설정이 복잡할 것으로 우려됨. (지랄스러울 듯)


[이슈]

LD_LIBRARY_PATH 이슈 : http://jetzt.tistory.com/332

DLL 만들기 : http://mamma.tistory.com/32

선택한 JNI 예제 : http://openshareit.tistory.com/entry/JNI-%EC%A2%85%EA%B2%B0%EC%9E%90-%EB%AC%B8%EC%84%9C

parameter 이슈 : http://stackoverflow.com/questions/4611365/compiling-c-file-that-uses-jni-h

linux 환경 변수 설정 이슈 : /etc/profile 수정

윈도우 환경 변수 설정 이슈 : PATH 설정(javah 있는 위치, gcc 있는 곳)

jni.h , jni_md.h 위치 이슈 : 개발 중인 폴더에 옮김