본문 바로가기
Linux

라즈베리파이에서 JNI를 해보자

by GGoris 2015. 6. 4.
반응형

라즈베리파이에서 JNI를 사용해보자


먼저 jdk 설치가 되어있어야합니다.



JNI는 C/C++라이브러리를 Java에서 사용할 수 있도록

 공유 라이브러리를 만들어주는 기능을 합니다.


JNI의 목적은 

코드의 재사용성의 용이함,

성능 향상,

하드웨어 의존적 코드에 대한 문제 해결

입니다.


JNI를 통해 만들어진 C라이브러라는

java코드상에서 Wrapper객체를 통해 호출 될 수 있습니다.


JNI 라이브러리를 만들기 위해서는 Wrapper클래스

라이브러리용 c파일을 만들어 주어야 합니다.


JNI를 이용한 java용 c library는 다음과같은 순서로 작성됩니다.

1. 함수 선언이 담긴 Wrapper 클래스 작성

2. Wrapper 헤더 생성

3. 함수를 구현한 c코드 작성

4. c코드를 이용해 라이브러리 빌드

5. java코드에서 사용




예제로 문자열을 print하는 간단한 기능으로 구현 하도록 하겠습니다.


먼저 Wrapper클래스입니다.


1
2
3
4
5
6
7
8
9
10
class Wrapper {
    //static block
    static {
        System.loadLibrary("greeting"); //libgreeting.so
 
    }
 
    //Native function declaration.
    public native void print();
}
cs



wrapper 클래스의 구조는

static 블럭

Native 함수 정의 부분으로 나누어 집니다.


static블럭에서는 라이브러리를 불러오는 초기화와 같은 역할을 하게 됩니다.

native정의 블럭에서는 사용될 함수의 원형을 작성하게 되는데 

이때 native키워드를 사용합니다.


이 Wrapper를 javac를 이용해 컴파일 해줍니다.

$ javac Wrapper.java


코드상 문제가 없다면 .class파일이 생성됩니다.

이것을 javah를 이용해 c에서 사용가능한 헤더로 만들어 줍니다. .class는 붙여주지 않습니다.

$ javah Wrapper


결과물로 Wrapper.h가 생성된 것을 확인하실 수 있습니다.


Wrapper.h입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Wrapper */
 
#ifndef _Included_Wrapper
#define _Included_Wrapper
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Wrapper
 * Method:    print
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_Wrapper_print
  (JNIEnv *, jobject);
 
#ifdef __cplusplus
}
#endif
#endif
cs



함수의 선언이 자동으로 만들어집니다.


함수이름 규칙은 아래와 같습니다.

Java_패키지_랩퍼이름_함수이름__인자(JNIEnv *, jobject, ...)

1. Java는 jni를 사용 했다는 접두어 정도로 보시면 될것 같습니다.

2. 패키지이름은 사용하지 않았으므로 생성되지 않았습니다.

3. 랩퍼이름은 우리가 작성한 Wrapper클래스의 이름이 됩니다.

4. 함수의 이름은 Native블럭에서 작성한 함수의 이름입니다.

5. 마지막으로 _2개를 통해 인자의 키워드가 접미어로 붙는데 현재작성한 

   코드에서는 인자를 받지 않으므로 생성되지 않습니다.

6. 추가적인 인자가 2개 추가됩니다. JNIEnv *와 jobject입니다.

   만약 랩퍼 클래스에서 2개의 인자가있었다면,

   JNIEnv와 jobject가 추가되어 총 4개의 인자를 받는 형태가 됩니다.


위의 규칙은 c++의 mangling naming과 유사합니다.

여기까지 랩퍼 클래스와 그를 통해 생성된 .h에 대한 작성과 설명이었습니다.




이 헤더 파일을 이용해 c코드를 작성합니다.

작성할 c코드에는 .h에서 만들어졌던 함수의 정의를 작성합니다.

Wrapper.h의 함수를 정의 해줍니다


1
2
3
4
5
6
7
#include <stdio.h>
#include "Wrapper.h"
 
JNIEXPORT void JNICALL Java_Wrapper_print(JNIEnv *env, jobject obj)
{
    printf("Hello, JNI!!\n");
}
cs




이제 이 c파일을 라이브러리로 만들어 주어야 합니다.
라이브러리의 네이밍 규칙은 아래와 같습니다.
 lib[라이브러리 이름].so

-shared옵션은 공윺 라이브러리 생성 옵션입니다.
$ gcc -o libgreeting.so greeting.c -shared

위의 명령을 사용하면 아래와 같은 애러를 확인 할 수 있습니다.

In file included from jni_type.c:2:0:

Wrapper.h:2:17: fatal error: jni.h: No such file or directory

 #include <jni.h>

                 ^

compilation terminated.


jni.h를 찾지 못하는 애러입니다.

jni.h는 jdk설치시 함께 설치 됩니다.

하지만 library 위치가 포함되어있지 않기때문에
gcc 빌드 시 위 내용을 찾지 못합니다.

따라서 gcc -I 옵션을 이용해 헤더의 경로를 명시해줍니다.

먼저 jni.h를 찾습니다.
$ sudo find / -name jni.h
/usr/lib/jvm/jdk-8-oracle-arm-vfp-hflt/include/jni.h

환경마다 경로가 다를 수 있기에 직접 검색하여 올바른 경로를 찾도록 합니다.


자 경로를 추가하여 다시 빌드해봅시다.
$ gcc -o libgreeting.so greeting.c -shared \
-I /usr/lib/jvm/jdk-8-oracle-arm-vfp-hflt/include/

또 문제가 발생합니다.
In file included from Wrapper.h:2:0,
                 from greeting.c:2:
/usr/lib/jvm/jdk-8-oracle-arm-vfp-hflt/include/jni.h:45:20: fatal error: jni_md.h: No such file or directory
 #include "jni_md.h"
                    ^
compilation terminated.

이번에는 ini_md.h를 찾지 못한다고 나옵니다.

jni_md.h는 machine dependenty가 정의된 파일 입니다.

각 운영체제에 맞는 jni_md.h가 존재합니다.

이녀석도 jdk를 설치할때 따로옵니다.

검색하여 추가해줍니다.

$ gcc -o libgreeting.so greeting.c -shared \
-I /usr/lib/jvm/jdk-8-oracle-arm-vfp-hflt/include/ \
-I /usr/lib/jvm/jdk-8-oracle-arm-vfp-hflt/include/linux

아무 메시지가 안뜨면 잘되었다는 것이겟죠!


jni를 통해 라이브러리를 만들어 주었고 
이 라이브러리를 이용해 java코드를 작성해 주면 됩니다.

이제 Main.java를 작성해봅니다.


1
2
3
4
5
6
class Main {
    public static void main(String [] args) {
        Wrapper wrapper = new Wrapper();
        wrapper.print();
    }
}
cs

사용방식은 wrapper 객체를 생성하고 구현한 함수를 사용하면 됩니다.


작성된 코드를 빌드하고

$ javac Main.java


실행 해줍니다.

$ java Main

Hello, JNI!!


잘 나오는군요!



만약 아래와 같은 애러가 뜬다면 라이브러리 경로가 안잡혀있는겁니다.

Exception in thread "main" java.lang.UnsatisfiedLinkError: no jni_type in java.library.path

        at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1857)

        at java.lang.Runtime.loadLibrary0(Runtime.java:870)

        at java.lang.System.loadLibrary(System.java:1119)

        at Wrapper.<clinit>(Wrapper.java:3)

        at Main.main(Main.java:3)


아래의 명령을 통해 현재 디렉터리를 라이브러리 디렉터리로 추가해줍니다.

$ export LD_LIBRARY_PATH=.


만약 라이브러리가 다른 디렉터리에 있다면 '.'대신 해당 디렉터리를 써야겟죠!



JNI를 이용한 간단한 라이브러리 작성 끝!


읽어주셔서 감사합니다.

반응형

'Linux' 카테고리의 다른 글

[ubuntu] 우분투에서 adb, fastboot 설치하기  (3) 2015.07.03
[RaspberryPI] gnu 컴파일러 4.9 설치! (gcc, g++)  (0) 2015.05.12
unp.h 사용하기  (2) 2015.05.06
우분투 eth0 고정 ip 설정  (0) 2015.05.06
vim 자동 정렬 하기  (0) 2015.04.24

댓글