본문 바로가기
  • AI (Artificial Intelligence)
Programming/Java

JNI사용하기 (여기서는 java + c, 사용툴 : Visual studio)

by 로샤스 2014. 2. 26.

<JNI>자바에서 c언어 사용하기

 

자바의 제약사항

자바는 운영체제에 독립적이기 위하여 자바가상머신을 이용한다. 이는 운영체제에 독립적이라는 편의성을 제공하지만, 한편으론 운영체제에서 사용할 수 있는 모든 기능을 끌어와 쓸 수 없다는 단점이 존재한다. 또한 자바는 바이트 코드로 된 프로그램이기 때문에 c c++등의 프로그램으로 만들어진 기계어 코드에는 속도가 느릴 수 밖에 없다.

 

자바의 제약사항을 덜어주는 JNI?

JNI는 자바 이외의 언어로 작성된 프로그램을 자바가상머신 위에서 실행 할 수 있도록 인터페이스를 제공해 주는 것이다. 따라서 JNI를 활용하면, 자바에서는 일부분의 제약이 있었던 운영체제만의 좋은 기능을 활용 할 수 있을 뿐만이 아니라, 여러 시스템을 아우를 수 있다.

 

미리 알아두어야 할 내용들

 

라이브러리 :

컴퓨터 프로그램에서 자주 사용되는 부분 프로그램들을 모아 놓은것으로, 자유롭게 이용할 수 있도록 구성되어 있다.

 

정적 링크 라이브러리 :

라이브러리를 추가할 때 목적코드와 사용할 링커에 의해 연결 되면서 하나의 실행 파일을 만든다. 라이브러리 코드는 복사되어 실행파일의 일부분이 되기 때문에 라이브러리를 추가할수록 용량이 점점 커져서 메모리의 효율성이 떨어진다.

 

동적 링크 라이브러리(Dynamic Link Library, DLL) :

라이브러리를 어디에든 다양하게 링크하여 사용할 수 있다는 의미를 지닌다.

이것은 실행 시점에 호출이 된다. 그리고 실행 파일에 라이브러리 코드가 복사되는 것이 아니고, 라이브러리를 통해 실행시킬 부분을 다른 파일로 구분하여 만들고, 각각의 두 프로그램은 하나의 DLL파일을 공유하여 사용된다. 만들기 까다롭지만 메모리 사용의 효율성과 여러 장점으로 인해 윈도우 운영체제에서는 DLL을 폭넓게 사용하고 있다.

 

DLL만들기 약간의 설명 : DLL은 독립적으로 실행되지 않고 클라이언트로부터 호출을 받아야 한다.

DLL에서는 함수를 내놓기 위한 Export, 클라이언트에서는 DLL함수를 얻어올 Import를 작성해야 한다.

 

Native Method : 컴파일러에게 실제 이 메소드의 구현은 외부에 다른 언어로 구현 될 것이라는 것을 명시하는 것이다.

 

JNI를 사용하기 위한 단계(여기서는 JAVA + C)

자바가 이용 가능한 c언어 파일(동적 라이브러리, DLL)을 만들기 위해서는 먼저 자바가 이해하는 c코드의 작성을 위해 자바로 c언어 헤더파일을 만들고(javah로 컴파일된 헤더파일), 그것을 c언어에 include시켜서 c언어를 작성하여 컴파일을 하면, 자바와 링크가 가능한 c언어 dll이 만들어 진다.

1단계 : Native Method를 선언하는 자바 클래스 작성

2단계 : 1단계에서 작성한 클래스 컴파일

3단계 :javah를 사용해서 Native Method가 사용할 헤더 파일 생성

4단계 : C언어로 Native Method  실제 구현

5단계 : 3단계의 헤더파일을 추가하여 4단계의 c파일을 컴파일함

6단계 : 자바 프로그램 실행

 

 

c언어로 구성된 dll을 사용하여 자바에서 System함수를 사용하여 콘솔화면 깨끗하게 지워보기

 

1단계 : native method로 구현하는 java프로그램 만들기

 

import java.util.Scanner;

 

public class Testdll{

        static{

               System.load("c:/workspace/Testdll/src/Systemdll.dll");

        }

        public native void methodCls();

        public static void main(String[] args){

               Scanner sc = new Scanner(System.in);

               Testdll td = new Testdll();

               System.out.println("지워줘~~~~~~~~~");

               System.out.println("1 누르면 native메소드(여기선methodCls) system함수의 cls 실행하여 화면이 지워짐");

               int number = sc.nextInt();

               if(number == 1){

                       td.methodCls();

               }

        }

}

 

<위의 소스코드 분석해보기>

1번코드 : System.load("c:/workspace/Testdll/src/Systemdll.dll");

** c:/ 에서 '/' 은 보통 경로를 표시할 때 쓰는 '\'이 아님에 주의

위 코드는 괄호 안에 주어진 디렉토리의 파일로 접근하여 로딩해 주는 역할을 한다.

만약 dll파일을 자바컴파일한 파일과 같은 폴더에 놓을 경우 경로를 일일이 적을 필요 없이 아래와 같은 소스로 대체 가능하다.

System.loadLibrary("Systemdll");  (괄호 안에는 확장자명 필요 없이 파일 이름만 적을것)

이와 같은 소스를 통해서 Native Method가 구현되어 있는 라이브러리를 동적으로 로딩할 수 있다. 주의할 점은 라이브러리는 Native Method(2번코드)가 호출되기 전에 반드시 로딩 되어야만 한다. 그렇기 때문에 main메소드보다 먼저 실행되는 static블록을 사용하였다. (이때 라이브러리는 c언어로 작성한 dll파일이 될 것이다.)

괄호안의 이름은 dll파일이름과 반드시 같아야 한다.

 

2번코드 : public native void methodCls();

위 코드는 외부에서 실행 될 메소드 라는 것을 선언하는 것이다. 이렇게 선언되면 로드 된 라이브러리(1번코드)를 통해 다른 객체에서 외부에서 선언된 메소드를 호출(3번코드) 가능하게 된다.

”native”의 의미는 Native Method가 어떤 형태인지 자바 클래스 내에 명시하는 역할을 한다.

 

3번코드 : td.methodCls();

(먼저 메소드가 선언된 클래스를 실체화 한 후(td로 참조함) 사용하였다.)

‘methodCls’ 메소드를 호출함으로써 자바가상머신에게 Native Method를 호출하겠다고 알려 주게 된다.

 

2단계 : 컴파일하기

 

java Testdll.java

 

3단계 : 헤더파일 만들기

 

javah Testdll  (Testdll.java Testdll.class가 아닌것에 주의)

 

<Testdll.h파일 분석해보기>

아래 소스코드는 만들어야 되는 것이 아니라 javah로 만들어진 헤더파일을 열어 본 것이다

/* DO NOT EDIT THIS FILE - it is machine generated */

#include <jni.h>

/* Header for class Testdll */

 

#ifndef _Included_Testdll

#define _Included_Testdll

#ifdef __cplusplus

extern "C" {

#endif

/*

 * Class:     Testdll

 * Method:    methodCls

 * Signature: ()V

 */

JNIEXPORT void JNICALL Java_Testdll_methodCls

  (JNIEnv *, jobject);

 

#ifdef __cplusplus

}

#endif

#endif

 

 

 

4단계 :  C언어로 Native Method  실제 구현

 

<test.c> => c소스의 이름은 전혀 상관없음. visual studio사용시, 프로젝트 이름은 dll파일 이름과 같아지므로 프로젝트 이름만 신경쓸 것

#include<stdio.h>

#include<windows.h>

#include<jni.h>

#include"Testdll.h"

 

JNIEXPORT void JNICALL

Java_Testdll_methodCls(JNIEnv *env, jobject obj){

        system("cls");

        return;

}

위의 c코드는 3단계에서 만들어진 헤더파일(Testdll.h)을 참고하여 만들면 된다.

메소드 형태가 헤더파일에서 명시한 것과 똑같이 사용하면 됨. 단지 매개변수란에 이름만 적어주면 된다.(env,obj)

 

//Java_<클래스이름>_<메소드이름>

//메소드 이름 앞에 Java_ 라는 접두사가 붙는다

//클래스와 메소드를 구분해주는 ‘.’() ‘_’(언더바) 로 바뀐다.

//클래스는 패키지를 포함하여 전체 이름을 써준다.

//JNIEnv* 는 실행환경이 자바가상머신이라는 것이고, jobject는 클래스 자체를 말함.

 

 

5단계 : 위의 소스를 컴파일 하기

 

visual studio6.0 이상의 버전에서만 테스트 하였음.

1) 새로운 프로젝트를 생성하는데, Win32 Dynamic-Link Library(Win32 Dll)프로젝트로 생성할 것

   ** 컴파일 후 생성되는 dll파일은 프로젝트 이름과 같아지므로, 프로젝트 이름은 java코드에서 라이브러리 추가코드에서 선언한 이름으로 해야 접근이 가능함 1단계를 참고하면 ‘Testdll’로 참고하였으므로, 프로젝트 이름을 Testdll로 둠.

 

2) 프로젝트에 라이브러리로 연결시켜 실행할 c코드와 자바컴파일로 만든 헤더파일을 추가해야함

- Project -> Add To Project -> Files클릭

- 위에서 만든 c언어로된 Native Method코드(4단계), 3단계에서 만들어진 헤더파일을 추가할 것.

** 참고로 헤더파일과 C파일은 같은 폴더 안에 있어야 한다. 헤더파일만 추가시키고 C파일은 visual studio를 이용하여 새로 만들 경우, dll파일을 c파일이 있는 곳에 넣어줘야 한다.

 

3) 정상적인 컴파일을 위하여 자바 include 헤더파일의 경로를 추가해줌(jni.h사용하려고)

Visual studio6.0의 경우

- Tools -> Options클릭 -> Directories -> New클릭

- 자바가 설치된 곳의 include폴더가 있는 경로를 적어줌

           ex : C:\Program Files\Java\jdk1.6.0_14\include

- 자바가 설치된 곳의 include폴더가 있는 곳의 win32폴더가 있는 경로를 적어줌

        ex : C:\Program Files\Java\jdk1.6.0_14\include\win32

 

Visual studio2012의 경우

- ‘프로젝트 -> ‘(프로젝트이름)속성란 클릭 -> VC++디렉터리 -> ‘포함디렉터리클릭 -> 오른쪽에 나타나는 아래방향 삼각형 클릭 -> <편집>클릭 -> 줄추가(폴더그림)클릭

- 자바가 설치된 곳의 include폴더가 있는 경로를 적어줌

           ex : C:\Program Files\Java\jdk1.6.0_14\include

- 자바가 설치된 곳의 include폴더가 있는 곳의 win32폴더가 있는 경로를 적어줌

        ex : C:\Program Files\Java\jdk1.6.0_14\include\win32

 

4) 컴파일

주의 : 컴파일할 c소스(test.c)와 헤더파일(Testdll.h)은 같은 폴더 내에 있어야 한다.

 

5) 컴파일 후 완성된 dll소스(프로젝트 폴더안의 Debug폴더에 있음)를 실행시킬 자바파일이 있는 곳으로 가져다 놓을 것

 

6단계 : 자바프로그램 실행 -> 정상 실행되는지 확인가능

**이클립스에 실행할 때는 dll파일을 자바소스파일이 있는 src폴더안이 아닌 프로젝트 파일 안에 넣을 것!

위에서 작성한 프로그램을 이클립스에서 실행할 경우 화면을 지우는 작업이 제대로 실행되지 않으므로 콘솔창에서 직접 실행해볼 것.

 

 

 

 

 

 

 

 

출처 : http://blog.naver.com/st95041?Redirect=Log&logNo=40001613448

 

 

 

 

 

 

 

 

'Programming > Java' 카테고리의 다른 글

Spring에서 Json으로의 입출력 정리  (0) 2014.09.11
Jackson Tutorial - 2  (0) 2014.09.11
Jackson Tutorial - 1  (0) 2014.09.11
Jackson How-To: Ignoring Unknown Properties  (0) 2014.09.11
파싱(Parsing)강좌  (0) 2014.05.23

댓글