54
JNIプログラミング 餃子のまち 宇都宮 村沢 邦夫

Programming JNI

Embed Size (px)

Citation preview

Page 1: Programming JNI

JNIプログラミング

餃子のまち 宇都宮

村沢 邦夫

Page 2: Programming JNI

JNIとは?

• Java Native Interface• Javaからネイティブコードを、またはネイティブコード

からJavaを利用するための仕組み(ネイティブコードは基本的に C/C++ を想定)

• Javaコードとネイティブコードを分離(Javaから直接ネイティブコードは呼ばない。呼び出したい関数は、シェアードライブラリとしてラップする)

• JNIプログラミングって、ほとんどCプログラミング(?)

Page 3: Programming JNI

JNIの歴史1

• JDK1.0 JNI以前 不統一時代 Sun - Native Method Interface JVMの実装に依存してしまう。

(互換性に問題) Netscape - Java Runtime Interface 移植性は考慮されていた

が、JVMの実装についての考慮がたりない Microsoft - Raw Native Interface ガーベッジコレクタと相互的

に動作しなくてはいけない

Page 4: Programming JNI

JNIの歴史2

• JDK1.1

3社間で統一 JNI1.1が組み込まれる。すべてのJVM間でバイナリ互換

ただしMicrosoftはJNIをサポートせずSunに訴えられる。

• JDK1.2JNI1.2 Javaプラットフォームに新機能の組み込みが可能、効率的で安定したインタフェースを確立

• JDK1.4JNI1.4 呼び出しインタフェースの新しいエントリポイント追加、java.nioパッケージのサポート

Page 5: Programming JNI

なぜJNI?

• 既存のライブラリを使いたい• パフォーマンスを向上させたい• ハードウェアにアクセスしたい

Page 6: Programming JNI

JNIで出来ること

• メソッドの呼び出し• フィールドへのアクセス• オブジェクトの生成• 例外の発生と捕捉• JVMのロード• スレッドの同期

Page 7: Programming JNI

JNIプログラミング

① Javaコード作成

② javacでクラスファイル生成

③ javahでインクルードファイル生成

④ インクルードファイルを元にC/C++コード作成

⑤ シェアードライブラリ生成

Page 8: Programming JNI

HelloWorld (Javaコード)$ vi HelloWorld.javapublic class HelloWorld {

static {System.loadLibrary("HelloWorld");

}

//Hello World表示public native void show();

}

$ javac HelloWorld.java

※System.loadLibraryでネイティブライブラリをロード※ネイティブメソッドにはnativeキーワードをつける

Page 9: Programming JNI

HelloWorld (ヘッダーファイル)$ javah HelloWorld$ cat HelloWorld.h/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class HelloWorld */

#ifndef _Included_HelloWorld#define _Included_HelloWorld#ifdef __cplusplusextern "C" {#endif/* * Class: HelloWorld * Method: show * Signature: ()V */JNIEXPORT void JNICALL Java_HelloWorld_show(JNIEnv *, jobject);

#ifdef __cplusplus}#endif#endif

Page 10: Programming JNI

HelloWorld (Cコード)$ vi HelloWorld.c#include "HelloWorld.h"

JNIEXPORT void JNICALL Java_HelloWorld_show(JNIEnv *env, jobject obj){

printf(“Hello World\n”);}

$ gcc -fpic -shared -o libHelloWorld.so -I $JAVA_HOME/include –I $JAVA_HOME/include/linux HelloWorld.c

※シェアードライブラリにすること

Page 11: Programming JNI

HelloWorldのテスト$ vi test.javapublic class test {

public static void main(String[] args) {HelloWorld hello = new HelloWorld();hello.show();

}}

$ javac test.java

実行$ java testHello World

※作ったシェアードライブラリはロード出来るようにしといてね (LD_LIBRARY_PATH)

Page 12: Programming JNI

nativeメソッドの引数

• Javaコード public native void show()

• Cコード JNIEXPORT void JNICALL Java_HelloWorld_show(JNIEnv

*env, jobject obj)

JNIEnv *env - JNIインターフェイスポインタ(privateなスレッドデータ、JNI関数へのポインタ)

jobject obj - オブジェクトへの参照(staticなメソッドの場合はクラスへの参照)

Page 13: Programming JNI

JNIインターフェイスポインタ

• ネイティブコードはJNI関数を用いてJVMへアクセスする。

• JNI関数はJNIインターフェイスポインタを用いて使用する。

Page 14: Programming JNI

HelloWorldのまとめ

• Javaコ ード はイ ンタ ーフェ イ スの定義• 処理の実装はCコ ード に• System.loadLibrary()でネイ ティ ブラ イ ブラ リ を

ロード• ネイ ティ ブメ ソ ッ ド にはnativeキーワード• C から Java へのアク セスはJNI関数

Page 15: Programming JNI

フィールドアクセス

$ vi Sample1.javapublic class Sample1 {

private int data;

static {System.loadLibrary("Sample1");

}

//フィールドに設定public native void setData(int val);

//フィールドから取得public native int getData();

}

Page 16: Programming JNI

フィールドに設定$ vi Sample1.c#include "Sample1.h"

// フィールドに設定JNIEXPORT void JNICALL Java_Sample1_setData(JNIEnv *env, jobject obj, jint val){

jclass clazz;jfieldID fid;

clazz = (*env)->GetObjectClass(env, obj);fid = (*env)->GetFieldID(env, clazz, "data", "I");(*env)->SetIntField(env, obj, fid, val);

}

Page 17: Programming JNI

フィールドから取得// フィールドから取得JNIEXPORT jint JNICALL Java_Sample1_getData(JNIEnv *env, jobject obj){

jclass clazz;jfieldID fid;jint data;

clazz = (*env)->GetObjectClass(env, obj);fid = (*env)->GetFieldID(env, clazz, "data", "I");data = (*env)->GetIntField(env, obj, fid);

return data;}

Page 18: Programming JNI

フィールドアクセスのテスト

$ vi test1.javapublic class test1 {

public static void main(String[] args) {Sample1 sample = new Sample1();sample.setData(123);System.out.println(“data = " + sample.getData());

}}

実行$ java test1data = 123

Page 19: Programming JNI

フィールド関連のJNI関数• jfieldID GetFieldID(JNIEnv *env, jclass clazz, const char *name,

const char *sig)フィールドID取得

• NativeType Get<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID)

フィールド値取得 ex) jint GetIntField()• void Set<type>Field(JNIEnv *env, jobject obj, jfieldID fieldID,

NativeType value)

フィールド値設定 ex) SetIntField()

※スタティックフィールド場合はGetStaticIntFieldIDのようにStaticが付く。※ネイティブコードからはJavaアクセス修飾子の制限を受けなくな

る。

Page 20: Programming JNI

フィールドアクセスのまとめ

① クラスを取得GetObjectClass()

② フィールドIDを取得GetFieldID()

③ フィールドの値を設定・取得Set<type>Field() / Get<type>Field()

※C から Java へのアクセスは JNI関数を使用する。

Page 21: Programming JNI

JNIのネイティブ型Java JNI

byte 8bit jbyte

short 16bit jshort

int 32bit jint

long 64bit jlong

float 32bit IEEE754 jfloat

double 64bit IEEE754 jdouble

char 16bit UNICODE jchar

boolean 8bit jboolean

void void

Object jobject

Class jclass

String jstring

配列 jarray j<type>Array

Throwable jthrowable

jvalue

jfiledID

jmethodID

Page 22: Programming JNI

型シグニチャJava シグニチャ

byte B

short S

int I

long J

float F

double D

char C

boolean Z

void V

<class>の完全修飾 L<class>;

<type>の配列 [<type>

メ ソ ッ ド シグニチャ (引数)戻値

例えば、次のメソッドシグニチャは

long hoge(int n, String s, int[] i) → (ILjava/lang/String;[I)J

Page 23: Programming JNI

メソッドコール・オブジェクト生成$ vi Sample2.javapublic class Sample2 {

static {System.loadLibrary("Sample2");

}

public Sample2() {}

public Sample2(int n) {System.out.println("create object success");

}

public void show() {System.out.println("method call success");

}

//オブジェクトの生成public native void createObject();

//メソッド呼び出しpublic native void callMethod();

}

Page 24: Programming JNI

オブジェクト生成$ vi Sample2.c#include "Sample2.h"

//オブジェクトの生成JNIEXPORT void JNICALL Java_Sample2_createObject(JNIEnv *env, jobject obj){

jclass clazz;jmethodID mid;jobject newObj;

clazz = (*env)->FindClass(env, "Sample2");mid = (*env)->GetMethodID(env, clazz, "<init>", "(I)V");newObj = (*env)->NewObject(env, clazz, mid, 0);

}

Page 25: Programming JNI

メソッドコール//メソッド呼び出しJNIEXPORT void JNICALL Java_Sample2_callMethod(JNIEnv *env, jobject obj){

jclass clazz;jmethodID mid;

clazz = (*env)->GetObjectClass(env, obj);mid = (*env)->GetMethodID(env, clazz, "show", "()V");(*env)->CallVoidMethod(env, obj, mid);

}

Page 26: Programming JNI

メソッドコール・オブジェクト生成のテスト

$ vi test2.javapublic class test2 {

public static void main(String[] args) {Sample2 sample = new Sample2();sample.createObject();sample.callMethod();

}}

実行$ java test2create object successmethod call success

Page 27: Programming JNI

クラス・オブジェクト関連のJNI関数• jclass GetObjectClass(JNIEnv *env, jobject obj)

オブジェクトのクラスを取得(判定)• jclass FindClass(JNIEnv *env, const char *name)

クラスをロード• jobject NewObject(JNIEnv *env, jclass clazz, jmethodID

methodID, ...)

オブジェクトの生成

Page 28: Programming JNI

オブジェクト生成のまとめ

① クラスをロードFindClass()

② コンストラクタのIDを取得GetMethodID()

③ オブジェクトを生成NewObject()

Page 29: Programming JNI

メソッド関連のJNI関数• jmethodID GetMethodID(JNIEnv *env, jclass clazz, const char

*name, const char *sig)

メソッドID取得• NativeType Call<type>Method(JNIEnv *env, jobject obj, jmethodID

methodID, ...)

メソッド呼び出し ex) CallVoidMethod()

※スタティックメソッドの場合はGetStaticIntMethodID()のようにStaticが付く。

※ネイティブコードからはJavaアクセス修飾子の制限を受けなくなる。

Page 30: Programming JNI

メソッドコールのまとめ

① クラスを取得GetObjectClass()

② メソッドIDを取得GetMethodID()

③ メソッド呼び出しCall<type>Method()

Page 31: Programming JNI

例外$ vi Sample3.javapublic class Sample3 {

static {System.loadLibrary("Sample3");

}

public static final int MAX = 10;private int[] datas = new int[MAX];

//例外を投げるpublic native void setData(int no, int data) throws ArrayIndexOutOfBoundsException;

//例外を捕まえるpublic native void showData(int no);

public int getData(int no) throws ArrayIndexOutOfBoundsException {try {

return datas[no];} catch (ArrayIndexOutOfBoundsException e) {

throw e;}

}}

Page 32: Programming JNI

例外を投げる$ vi Sample3.c#include "Sample3.h"

JNIEXPORT void JNICALL Java_Sample3_setData(JNIEnv *env, jobject obj, jint no, jint data){

jclass clazz;jfieldID fid;jint *dataElems;jintArray datas;jboolean isCopy;

clazz = (*env)->GetObjectClass(env, obj);fid = (*env)->GetFieldID(env, clazz, "datas", "[I");datas = (*env)->GetObjectField(env, obj, fid);dataElems = (*env)->GetIntArrayElements(env, datas, &isCopy);

if (no < 0 || no >= Sample3_MAX) {//例外投げるjclass clazz;jmethodID mid;jthrowable throwObj;

clazz = (*env)->FindClass(env, "java/lang/ArrayIndexOutOfBoundsException");mid = (*env)->GetMethodID(env, clazz, "<init>", "()V");throwObj = (*env)->NewObject(env, clazz, mid);(*env)->Throw(env, throwObj);return;

}

dataElems[no] = data;if (isCopy == JNI_TRUE) {

(*env)->ReleaseIntArrayElements(env, datas, dataElems, 0);}

}

Page 33: Programming JNI

例外を捕まえるJNIEXPORT void JNICALL Java_Sample3_showData(JNIEnv *env, jobject obj, jint no){

jclass clazz;jmethodID mid;jint data;jthrowable throwObj;

clazz = (*env)->GetObjectClass(env, obj);mid = (*env)->GetMethodID(env, clazz, "getData", "(I)I");data = (*env)->CallIntMethod(env, obj, mid, no);

//例外を捕まえるthrowObj = (*env)->ExceptionOccurred(env);if (throwObj != NULL) {

(*env)->ExceptionDescribe(env);(*env)->ExceptionClear(env);return;

}

printf("native data = %d\n", data);}

Page 34: Programming JNI

例外のテスト(1)$ vi test3.javapublic class test3 {

public static void main(String[] args) {Sample2 sample = new Sample2();try {

sample.setData(5, 123);System.out.println("data = " + sample.getData(5));sample.setData(10, 456);

} catch (ArrayIndexOutOfBoundsException e) {e.printStackTrace();

}sample.showData(5);sample.showData(10);

}}

Page 35: Programming JNI

例外のテスト(2)

実行$ java test3data = 123java.lang.ArrayIndexOutOfBoundsException

at Sample2.setData(Native Method)at test2.main(test2.java:7)

native data = 123Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException

at Sample2.getData(Sample2.java:14)at Sample2.showData(Native Method)at test2.main(test2.java:12)

Page 36: Programming JNI

例外関連のJNI関数• jint Throw(JNIEnv *env, jthrowable obj)

例外を投げる• jthrowable ExceptionOccurred(JNIEnv *env)

例外が発生してるか調べる• void ExceptionDescribe(JNIEnv *env)

例外のスタックトレースをプリント• void ExceptionClear(JNIEnv *env)

例外のクリア

※Throw()しても実際に例外を投げるのはネイティブメソッドを抜けた後

Page 37: Programming JNI

例外のまとめ(投げる)

① クラスをロードFindClass()

② コンストラクタのIDを取得GetMethodID()

③ オブジェクトを生成NewObject()

④ 例外を投げるThrow()

Page 38: Programming JNI

例外のまとめ(捕まえる)

① 例外が発生しているが調べる。 ExceptionOccurred()

② 例外処理③ 例外をクリア

ExceptionClear()

Page 39: Programming JNI

CからJavaを呼ぶ

$ vi Sample4.javapublic class Sample4 {

public void show() {System.out.println(“JVM World");

}}

Page 40: Programming JNI

CからJavaを呼ぶ(Cコード)(1)$vi Sample4.c#include <jni.h>

int main(int argc, char *argv[]) {JNIEnv *env;JavaVM *jvm;JavaVMInitArgs vm_args;

jclass clazz;jobject obj;jmethodID mid;

jint ret;

vm_args.version = JNI_VERSION_1_2;

JNI_GetDefaultJavaVMInitArgs(&vm_args);

ret = JNI_CreateJavaVM(&jvm, (void **) &env, &vm_args);if (ret < 0) {

fprintf(stderr, "Can't create JVM.");return 1;

}

Page 41: Programming JNI

CからJavaを呼ぶ(Cコード)(2)clazz = (*env)->FindClass(env, "Sample3");

mid = (*env)->GetMethodID(env, clazz, "<init>", "()V");

obj = (*env)->NewObject(env, clazz, mid);

mid = (*env)->GetMethodID(env, clazz, "show", "()V");

(*env)->CallVoidMethod(env, obj, mid);

(*jvm)->DestroyJavaVM(jvm);

return 0;}

実行$ ./Sample4JVM World

Page 42: Programming JNI

JNIの起動(呼び出し)API• jint JNI_CreateJavaVM(JavaVM **p_vm, JNIEnv **p_env,

void *vm_args)JVMのロード、初期化

• jint DestroyJavaVM(JavaVM *vm)

JVMのアンロード• jint JNI_GetDefaultJavaVMInitArgs(void *vm_args)

JVM初期化の為のデフォルト値取得

Page 43: Programming JNI

CからJavaを呼ぶのまとめ

① JVMの初期化データを設定 JNI_GetDefaultJavaVMInitArgs()

② JVMのロード・初期化 JNI_CreateJavaVM()

③ JNI関数を用いて処理④ JVMのアンロード

DestroyJavaVM()

Page 44: Programming JNI

文字列$ vi Sample5.javapublic class Sample5 {

static {System.loadLibrary("Sample5");

}

//Javaからの文字列表示public native void show(String str);

//Cから文字列取得public native String getMessage();

}

Page 45: Programming JNI

文字列(Javaから)$ vi Sample5.c#include <iconv.h>#include "Sample5.h"

// Javaからの文字列表示JNIEXPORT void JNICALL Java_Sample5_show(JNIEnv *env, jobject obj, jstring str){

const char *msg;jboolean isCopy;

msg = (*env)->GetStringUTFChars(env, str, &isCopy);if (isCopy == JNI_TRUE) {

iconv_t cd;size_t isize, osize;char euc[25];char *obuf = euc;

isize = (*env)->GetStringUTFLength(env, str) * 2;osize = sizeof(euc);cd = iconv_open("euc-jp", "utf-8");iconv(cd, &msg, &isize, &obuf, &osize);printf("%s\n", euc);(*env)->ReleaseStringUTFChars(env, str, msg);iconv_close(cd);

}}

Page 46: Programming JNI

文字列(Cから)// Cから文字列取得JNIEXPORT jstring JNICALL Java_Sample5_getMessage(JNIEnv *env, jobject obj){

const char *msg = "シー ワールド";char utf[25];char *obuf = utf;iconv_t cd;size_t isize, osize;

isize = strlen(msg);osize = sizeof(utf);memset(utf, 0, osize);

cd = iconv_open("utf-8", "euc-jp");iconv(cd, &msg, &isize, &obuf, &osize);iconv_close(cd);

return (*env)->NewStringUTF(env, utf);}

Page 47: Programming JNI

文字列のテスト$ vi test5.javapublic class test5 {

public static void main(String[] args) {Sample5 sample = new Sample5();sample.show("ジャバ ワールド");System.out.println(sample.getMessage());

}}

実行$ java test5ジャバ ワールドシー ワールド

Page 48: Programming JNI

文字列関連のJNI関数• jstring NewStringUTF(JNIEnv *env, const char *bytes)UTF-8の文字列からStringオブジェクトを生成

• const char* GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy)

StringオブジェクトからUTF-8文字列を取得• void ReleaseStringUTFChars(JNIEnv *env, jstring string,

const char *utf)GetStringUTFChars()で取得した文字列を開放

• jsize GetStringUTFLength(JNIEnv *env, jstring string)文字列の長さを取得

※C側で扱える文字コードに変換する必要あり(今回はiconvを使用した。)

Page 49: Programming JNI

文字列のまとめ(Javaから)

① Stringオブジェクトから文字列を取得GetStringUTFChars()

② Cでの文字コードへ変換(今回はiconvを使った)

③ 文字列を開放ReleaseStringUTFChars()

Page 50: Programming JNI

文字列のまとめ(Cから)

① Javaの文字コードへ変換(今回はiconvを使った)

② Stringオブジェクトを生成NewStringUTF()

Page 51: Programming JNI

スレッドの同期方法

• nativeメソッドにsynchronizedキーワード• JNI関数 MonitorEnter/MonitorExit を使用(Java synchronized文で囲むのと同等)

• JNI関数 Call<type>Method を用いて wait/notifyメソッドを呼び出す

Page 52: Programming JNI

今日のまとめ

• 処理の実装はCコード側• C から Java へのアクセスはJNI関数• やっぱりCプログラミングだった(?)• JNIプログラミングは難しくない(?)• でも面倒だ

Page 53: Programming JNI

参考文献

• JNI : Java Native Interfaceプログラミング著者 ロブ・ゴードン 出版社 ピアソン

• JDKドキュメントhttp://java.sun.com/j2se/1.4/ja/docs/ja/guide/jni/index.html

Page 54: Programming JNI

おわり

Ver.1.0 2002/08/22Ver.1.1 2002/08/27