이번에는 Java Native Interface 에 대한 이야기를 해보자. 잘 아시다시피 자바는 JVM이라는 가상 기계를 만들어내어서 자바바이트코드를 실행한다. 즉, 리눅스나 윈도우처럼 추상화계층을 두고 프로그램을 실행하는 체계이기 때문에 어느 플랫폼이든 동일한 실행이 보장된다. 잔디밭이든, 자갈밭이든, 모래밭이든 푹신한 돗자리를 그위에 올리면 항상 돗자리인 상태가 되는 것과 같은 이치다. 그러나, 자바 가상 머신으로 바이트코드를 실행하면, 가상머신이 제공하는 기능만을 사용하여야 하기 때문에 윈도우나 리눅스에서 제공하는 기능들을 활용하기가 어렵다. 여기에서는 윈도우와 리눅스에서의 JNI활용법을 확인해보기로 한다. JNI의 활용을 위해서는 다음의 사항을 이해하여야 한다. 1. C언어를 기반으로 윈도우의 동적라이브러리(dll)을 만들 수 있어야 한다. 2. 리눅스를 기반으로 할 경우 리눅스의 동적라이브러리(so)를 만들 수 있어야 한다. 3. C언어 문법을 알아야 한다. 4. C언어와 자바의 데이터타입 매핑을 이해하여야 한다. 5. 환경변수 등의 기본 사항을 알고 있어야 한다. 1. JNI 작성절차 먼저, JNI를 만들어내는 절차를 확인하자. 1). 자바로 네이티브 동적 라이브러리를 호출하는 루틴 작성 2). 네이티브 함수 선언 3). JDK에서 제공하는 javah를 사용하여 C/C++ 헤더파일 생성 4). 동적라이브러리 코드 작성 5). jre를 사용하여 라이브러리를 포함한(-D 옵션) 클래스 파일 실행 2. 윈도우에서의 JNI작성 Java Native 소스코드 작성 내용은 C기반으로 멀티스레드를 사용하는 것으로 하겠다. 자바소스파일을 메모장에서 작성한 후 명령프롬프트에서 컴파일 했다. 이제 두 번째로는 C에서 사용할 수 있도록 헤더파일을 작성하는 것이다. 직접 작성해도 될 것이지만, 여러 설정 등을 위해서 jdk의 javah를 사용하여 클래스파일을 지정해 만들어 주면된다. 이때 옵션 지정이 필요한데, 옵션은 –JNI(기본값)를 지정하면된다. 이제 헤더파일을 확인해보자. 헤더파일을 열어보면 시스템이 생성한 파일이고, 편집하지 않을 것을 지시하고 있다. 이제 C언어를 이용해서 멀티스레드 프로그램을 작성한다. Visual Studio를 사용했다. 이때 주의하여야 할 사항은 자바플랫폼에 맞추어서 라이브러리를 제작해야 한다는 것이다. 자바의 버전은 jvm 으로 확인한다. java –version을 입력하면 된다. 위 시스템은 64비트로 구동되는 것을 확인할 수 있다. 그러므로 라이브러리도 64비트로 작성해야 한다. Visual Studio의 경우 64비트 프로젝트로 설정해야 한다. 프로젝트 속성을 확인하면 64비트로 지정 할 수 있다. 만약 자바가 32비트라면 플랫폼을 win32로 지정하면된다. 또한, 그냥 디버그 모드로 작성했다. 필요한 경우 릴리즈 모드로 작성하면 될 것이다. 32비트에서 64비트로 지정하려면 구성관리자를 선택한다. 구성관리자를 선택하면 다이얼로그가 하나 실행되는데, 플랫폼 콤보박스에서 새로만들기를 선택하여 새 프로젝트 플랫폼을 x64로 지정한다. 완료되면 헤더파일을 추가하자. 헤더파일은 위에서 언급했던대로 javah로 만들어낸 헤더파일을 사용하면 된다. 다시말하면, 새로운 헤더파일이 아니라, 기존헤더파일을 추가해야 한다. 헤더파일이 추가되면 #include된 jni.h를 볼 수 있는데, 시스템 헤더파일형태로 지정되어 있다.(헤더파일이 <>으로 되어 있으면 시스템 라이브러리, “”로 되어 있으면 프로젝트 디렉토리다) 따라서, 헤더파일들을 별도로 설정하지 않을 거면 프로젝트 속성을 변경해야 한다. 위 그림처럼 프로젝트 속성에서 VC++ 디렉토리-포함 디렉토리(include directory)를 편집하여 jdk의 include 폴더와, win32폴더를 추가해준다.(안하면 jni.h와 다른 헤더파일을 찾지 못한다.) 이제 본격적으로 DLL파일을 작성해보자. dll 작성을 위해서는 여러 가지 지식을 알고 있어야 하지만, 그부분은 차차 확인해보기로 하고, 여기에서는 그냥 함수만 사용하여 체크한다.(별도로 DLL작성에 대한 내용을 올릴 예정이다) dll을 작성하려면 새프로젝트를 선택하여 dll프로젝트로 선택하면 된다. 만약 Visual Studio 2015 이상이라면 윈도우 데스크톱마법사를 선택하여 실행하면 DLL을 선택할 수 있다. dll함수는 기본적으로 앞쪽에 __declspec(dllexport)를 붙여서 작성해야 한다. 만약 소스파일이 *.cpp라면 extern “C” __declspec(dllexport)를 함수 앞쪽에 붙여준다. 여기에서 사용되는 C 네이티브 함수는 다음과 같다. 1. ThreadControl : 스레드용 제어용 스레드 함수, 멀티스레드를 구동시킨다. 2. ThreadExe : 스레드 코드 3. GetStdHandle() : 윈도우 API, 콘솔(명령프롬프트)의 핸들을 얻는다. 4. SetConsoleCursorPostion() : 현재 실행중인 콘솔(명령프롬프트)의 출력위치를 지정한다. 5. CreateThread() : 윈도우 API, 스레드를 생성하는 함수, MS에서는 _beginthread()함수 사용을 권장하고 있다. 사용되는 데이터 타입은 다음과 같다. 1. HANDLE : API 타입, 스레드 제어용 핸들 및 콘솔 출력 제어용 핸들 2. COORD : API 타입, 화면 좌표를 지정하는 구조체, X, Y멤버를 갖는다. 스레드 함수와 API는 MSDN에 자세히 설명되어 있으니 참고하자.
이프로그램은 두 개의 스레드가 0.5초에 한번씩 숫자를 카운팅하여 표시하며 100번 출력하면 종료된다. 좀더 그럴싸한 프로그램이면 좋겠지만, 그렇게되면 소스의 양이 많아지고, 보기에 복잡해질 것 같아 단순화시켰다. 이제 컴파일을 하자. Visual Studio의 메뉴에 보면 빌드에서 프로젝트 빌드만 수행해준다. 이제 디버그 폴더를 보면 dll파일이 생성된 것을 확인할 수 있다 .이제 스레드를 실행하는 추가 자바소스를 작성하자.
소스 작성이 완료되었으면 컴파일 해준다. 이제 실행만 하면된다. jvm을 구동하여 실행해보자. c를 이용한 멀티스레드가 실행되고 있다. 공유리소스가 있다면, 윈도우에서 가지고 있는 여러 가지 동기화 기법을 사용해도 되고, 윈도우의 비동기 IO(I/O complete port) 기법 등 운영체제가 지원하는 여러 가지 고급 요소들도 사용할 수 있을 것이다. 2. 리눅스에서의 JNI 활용 먼저 윈도우에서 했던 것처럼 cat를 사용하여 자바소스 파일을 생성하자. 다음은 위그림 처럼 c헤더파일을 생성하여야 한다. 리눅스에서는 openjdk-11을 사용했는데, 여기에서는 javah가 사라지고 javac 컴파일러가 JNI헤더파일 생성을 대신하고 있다. 헤더파일 생성은 javac –h 스레드 소스파일을 사용하면 된다. 이제 다음차례는 c 동적라이브러리를 제작하여야 한다. 우선 컴파일러 gcc가 설치되었는지 체크하자. 없을 경우에는 아래처럼 apt install gcc를 사용하여 설치하면 된다. 헤더파일 생성이 완료되면 리눅스에서 네이티브 C코드를 작성한다. 이제 vi를 사용하여 C소스코드를 작성하자.
사용된것중 API는 다음과 같다. - threadExe() : 스레드코드를 담아놓은 함수 - threadcontrol() : 스레드를 실행하도록 제어하는 함수 - pthread_t : 실행되는 스레드에 대한 정보를 담아놓은 변수 - pthread_join() : API, 스레드가 종료될때까지 대기하는 대기함수 이제 jdk의 jni위치를 확인해보자 먼저 apt로 jdk가 어떤 이름으로 설치되었는지 확인하자 그다음 find 명령을 사용하여 openjdk-11 이 포함된 디렉토리를 찾는다. 찾아본 결과 위의 시스템에서는 /usr/lib/jvm/java-11-openjdk-i386이 경로였다. 이제 *so 동적라이브러리를 컴파일해보자. 컴파일할 때에는 동적라이브러리를 만들도록하기위해 –fPIC 옵션을 사용하고, jni.h와 기타 부가적인 리눅스용 헤더파일을 포함시켜야 하기 때문에 –I옵션으로 jni.h가 있는 경로를 지정한다.
위의 –I/root 위치에는 javac가 만든 c헤더파일이 존재하기 때문에 추가했다. 위 그림에서 thread_so.o 파일이 생성되었음을 확인할 수 있다. 이제 *.so파일로 생성해보자. 리눅스에서 라이브러리를 생성할 경우에는 기본적으로 이름앞에 lib를 포함시켜서 만들어야 한다. 라이브러리 옵션은 –shared를 주고 –o옵션을 사용하여 이름을 지정한다.
이제 공유라이브러리가 생성되었다면 자바실행코드를 작성하자 작성된 자바코드를 클래스 파일로 컴파일한다.
자, 이제 마지막으로 윈도우와 마찬가지로 jvm을 호출하여 실행한다.
이렇게 Java와 C를 같이 사용하게되면 자바에서 제공하지 못하는 기능들을 운영체제로부터 제공받아 활용할 수가 있게된다.. OS가 제공하는 메모리매핑, 네임드파이프, 비동기IO, 스레드 동기화 등 많은요소들을 활용할 수 있다. 문의사항있으면 댓글이나 전화주세요. |
'학습관련Tip , 미니강좌 > IT.SW' 카테고리의 다른 글
[바이트]에서 전하는 JavaScript Prototype 활용 (0) | 2020.04.09 |
---|---|
[바이트에서 전하는] Java(자바)의 클래스패스 (0) | 2020.04.09 |
JAVA Tomcat Eclipse oracle 설치 및 설정(4) - oracle 11g 다운로드 및 설치/계정 등 설정 (0) | 2020.03.27 |
JAVA Tomcat Eclipse oracle 설치 및 설정(3) - Tomcat 다운로드 및 설정 (1) | 2020.03.26 |
JAVA Tomcat Eclipse oracle 설치 및 설정(2) - Eclipse(이클립스) 다운로드 및 설정 (0) | 2020.03.23 |
댓글