テリ・J.アンドリューズ(Terri J. Andrews)によると、『悪夢は網目に引っかかったまま夜明けと共に消え去り、良い夢だけが網目から羽を伝わって降りてきて眠っている人のもとに入る』とされる。 また、『良い夢は網目の中央にある穴を通って眠っている人に運ばれてくるが、悪夢は網目に引っかかったまま夜明けと共に消え去る』とも言う。
⇧ 良い夢を見ることのないまま、時の流れを感じ続ける、どうもボクです。
「ドリームキャッチャー」ってそんな意味があったんですね、夢をフィルターしてくれていたとは目から鱗です!
そんなネイティブの叡智を知ったところで、今回もJavaについてです。
ちなみに、
⇧ まさかの「CentOS」系に衝撃のニュースが!
激震が走っておりますが...
レッツトライ~。
JVM(Java Virtual Machine)はOS(Operation System)のファイルシステムを参照してると
事の発端は、
⇧ を実施していて、「gcc」でコンパイルした「DLL(Dynamic Link Library)」の場所が見つからんって言われたんですよね。
ちなみに、
⇧ JDK 10 からは「javah」が削除されて、「javac」の「h」オプションを使って「DLL(Dynamic Link Library)」を作る感じになった模様、時の流れ~。
で、ネットで調べてたところ、
Java では System
または System
を使用してネイティブライブラリー (DLL) をロードすることができます。これらにはライブラリーのフルパスを指定するか、 ライブラリー名のみを指定するかという違いがあります。
ライブラリーのフルパスを指定する場合は System
を使用します。
ライブラリー名を指定する場合は System
を使用します。このとき、 java
プロパティーで設定されているパスからライブラリーが検索されることになります。
⇧ 上記サイト様によりますと、「ライブラリーのフルパスを指定」ができるってあったんですよ。
な~に~!やっちまったな!(クールポコ先輩~)
というのも、「パス」ってのは、「OS(Operation System)」の「ファイルシステム」によって差異があるんですと。
Linux環境にJDKをインストールして発覚
何で気付いたかと言うと、Linux環境にはWindowsのようにドライブって概念がないんですよ。
つまり、「System.load()」を使う場合は、
OS(Operation System)がWindowsの場合は、
System.load("C:/mylibs/foobar.dll");
OS(Operation System)がLinuxの場合は、
System.load("/home/foobar.so");
みたいな感じで、OSによって変わってきますと。
そう!、「System.load()」は、OSに依存してしまうってわけなんですよ。
ちなみに、「System.loadLibrary()」なら大丈夫かと言うと、さにあらず。
「java.library.path」ってのに影響されますと。
なので、「java.library.path」の中身を確認してみました。
■Linux環境
CentOS 8(64bit。「Docker Hub」の公式の「centos」イメージ)
Java:OracleJDK 15のLinux版(RPMパッケージ)
■Windows環境
Windows 10 Home(64bit)
Java:OracleJDK 15のWindows版(アーカイブ)
ってな感じで、 OSによってまるきり変わってくるらしいですと。
Linux環境に至っては、
⇧ そもそも存在しないディレクトリも「java.library.path」に含まれてるっぽい...
そして「java.library.path」の追加を試みるも、
⇧ 駄目っすな...
「java.library.path」の1つである「/usr/java/packages/lib」ディレクトリを用意して、ライブラリを配置してみたけど、
⇧ 見つからんって言われるし...
Javaからdllを使う場合に java.lang.UnsatisfiedLinkErrorが発生するので原因と回避方法を調べました。 OSが32ビットの時は問題なく動作していたが64ビットOSに変更した際動かくなったという情報だけはありました。
javaからDLLをコールすると java.lang.UnsatisfiedLinkErrorが発生する - moのブログ
エラーの原因は単純で64ビットJavaから32ビットのdllを呼ぼうとしているのが原因でした。
javaからDLLをコールすると java.lang.UnsatisfiedLinkErrorが発生する - moのブログ
⇧上記サイト様を参考に確認してみる。
⇧ 駄目でした...
ばっちり64bitで「DLL(Dynamic Link Library)」作成されてるっぽい...
念のため、JDKも確認してみるけど、
⇧ ばっちり64bitでした...
あとは、「DLL(Dynamic Link Library)」の作り方かな。
javac -h [C言語のヘッダファイルの出力先のディレクトリを指定] Answer019.java
gcc -shared -D__int64='long long' -I/usr/java/jdk-15.0.1/include -I/usr/java/jdk-15.0.1/include/linux -c Answer019.c -o libAnswer019.so
ちなみに、自分の環境では、以下がJNI(Java Native Interface)で必要なDLL(Dynamic Link Library)ライブラリを作成するために、使用するファイル群かと。
/usr/java/jdk-15.0.1/include /usr/java/jdk-15.0.1/include/classfile_constants.h /usr/java/jdk-15.0.1/include/jawt.h /usr/java/jdk-15.0.1/include/jdwpTransport.h /usr/java/jdk-15.0.1/include/jni.h /usr/java/jdk-15.0.1/include/jvmti.h /usr/java/jdk-15.0.1/include/jvmticmlr.h /usr/java/jdk-15.0.1/include/linux /usr/java/jdk-15.0.1/include/linux/jawt_md.h /usr/java/jdk-15.0.1/include/linux/jni_md.h
そして、衝撃の結末...
ファイル名をlibで始まるように変更したところうまくいきました.
Javaで呼ぶ方では「lib」付けなくていいんですね.
⇧ な、何やて~!
何なの、その特殊ルール、
Java Native Interface 6.0の仕様では、Solaris、Linux、およびWindowsプラットフォーム上でJNIメカニズムを使用してオブジェクトを表示するためにAWTパッケージがどのように設計されているかについて説明します。
https://docs.oracle.com/javase/jp/8/docs/technotes/guides/jni/index.html
System.loadLibrary
の引数は、プログラマによって任意に選択されたライブラリ名です。このシステムは、標準であってもプラットフォーム固有の方式に従ってライブラリ名をネイティブ・ライブラリ名に変換します。たとえば、Solarisシステムはpkg_Cls
という名前をlibpkg_Cls.so
に変換するのに対して、Win32システムは同じpkg_Cls
という名前をpkg_Cls.dll
に変換します。
https://docs.oracle.com/javase/jp/8/docs/technotes/guides/jni/spec/design.html
⇧ う~ん、「Linux」も「Solaris」と同様と考えて良いのであれば、
- 「libpkg_Cls.so」 ってライブラリを作成
- JavaファイルでSystem.loadLibrary()の引数には、「pkg_Cls」を設定
ってことになるのか...
修正してみて実施したところ、
Java HotSpot(TM) 64-Bit Server VM warning: You have loaded library /usr/java/packages/lib/libAnswer019.so which might have disabled stack guard. The VM will try to fix the stack guard now. It's highly recommended that you fix the library with 'execstack -c <libfile>', or link it with '-z noexecstack'. Exception in thread "main" java.lang.UnsatisfiedLinkError: /usr/java/packages/lib/libAnswer019.so: /usr/java/packages/lib/libAnswer019.so: only ET_DYN and ET_EXEC can be loaded ...省略
どっちにしろエラーやんけ~!
ただ、エラー前の警告の内容を見てみると、「ライブラリ」を修正することを進めるってあるんで、 「gcc」でのコンパイルのオプションを追加すればいけそう?
関係ありませんでした...
ただ、Linux環境の場合は、
⇧ 「java.library.path」の他に「LD_LIBRARY_PATH」ってやつを設定してあげる必要があるみたいね。
⇧ 命名規則とかもあるようね。
そして、申し訳ない、「Answer019.c」でタイポ(打ち間違い)がありました。
■誤
#include <stdio.h> #include <stdlib.h> #include "Answer019.h" // ランダムで返す整数の最大値 #define MAX 7 JNIEXPORT jint JNICALL java_Answer019_randomNum (JNIEnv *env, jobject obj) { return rand() % MAX +1; }
■正
#include <stdio.h> #include <stdlib.h> #include "Answer019.h" // ランダムで返す整数の最大値 #define MAX 7 JNIEXPORT jint JNICALL Java_Answer019_randomNum (JNIEnv *env, jobject obj) { return rand() % MAX +1; }
おわかりいただけただろうか?
そう、7行目の「Java_Answer019_randomNum」の部分の先頭が小文字の「j」になってしまっていたのである...
自分が悪いんだけど、これに気付いた時には絶望しか感じませんでしたね...
修正後、「gcc」 でライブラリを作成して、
gcc -fPIC -I"/usr/java/jdk-15.0.1/include" -I"/usr/java/jdk-15.0.1/include/linux" Answer019.c -shared -o libAnswer019.so
Javaを実行(クラスファイルを既に作成済みです)
java Answer019
⇧ 無事動きました!
というわけで、タイポ(打ち間違い)には気を付けましょう。
コピペできる状況の時は、コーディングしてくれた方に感謝の気持ちを抱きつつ積極的にコピペさせていただきましょう。
1人で生きてるんじゃないんだよ~!、みんなの支えがあってこその自分だよ~!
っていう気持ちになったところで、
今回はこのへんで。