解析java.library.path和LD_LIBRARY_PATH的介紹與區(qū)別
近期要將算法部署到一個機群的虛擬主機(Debian 9.1 gcc 6.3.0)上,采用的是Java + JNI + shared library的方式來完成底層算法能力的部署。
其中需要用到各種第三方庫,有從源碼編譯的,也有直接下載的so,包括OpenCV相關(guān)、TensorFlow相關(guān)、MKL以O(shè)penMP相關(guān)的動態(tài)庫。
遇到一個問題,libmklml_intel.so 這個庫只能放在 LD_LIBRARY_PATH中進(jìn)行加載,而不能通過java.library.path完成加載,所以有必要搞清楚這兩個路徑究竟有什么區(qū)別。
java.library.path官方文檔的定義是:List of paths to search when loading libraries從定義我們可以發(fā)現(xiàn),首先是一個list,也就是說可以包括多個地址,然后這些地址是用來幫助jvm搜索需要加載的庫文件的。
設(shè)置java.library.path最簡單的辦法就是在啟動jvm前通過java -Djava.library.path=path-to-your-libs設(shè)置這個全局變量。
作用那么這個地址具體是如何被使用的呢?當(dāng)我們調(diào)用System.loadLibrary(libname)時,會調(diào)用Runtime.loadLibary,然后調(diào)用java/lang/ClassLoader.loadLibrary。在ClassLoader.loadLibrary中,系統(tǒng)屬性java.library.path將會被獲取,并用來生成需要加載的庫的絕對路徑,然后將這個絕對路徑傳給本地方法來調(diào)用dlopen/dlsym并最終加載這個庫。如果加載失敗,會根據(jù)實際情況返回三個異常值:
SecurityException − if a security manager exists and its checkLink method doesn’t allow loading of the specified dynamic libraryUnsatisfiedLinkError − if the library does not existNullPointerException − if libname is null
可以參考OpenJDK的倉庫:
static void loadLibrary(Class fromClass, String name, boolean isAbsolute) {ClassLoader loader = (fromClass == null) ? null : fromClass.getClassLoader();if (sys_paths == null) { usr_paths = initializePath('java.library.path'); sys_paths = initializePath('sun.boot.library.path');}if (isAbsolute) { if (loadLibrary0(fromClass, new File(name))) {return; } throw new UnsatisfiedLinkError('Can’t load library: ' + name);}if (loader != null) { String libfilename = loader.findLibrary(name); if (libfilename != null) {File libfile = new File(libfilename);if (!libfile.isAbsolute()) { throw new UnsatisfiedLinkError( 'ClassLoader.findLibrary failed to return an absolute path: ' + libfilename);}if (loadLibrary0(fromClass, libfile)) { return;}throw new UnsatisfiedLinkError('Can’t load ' + libfilename); }}for (int i = 0 ; i < sys_paths.length ; i++) { File libfile = new File(sys_paths[i], System.mapLibraryName(name)); if (loadLibrary0(fromClass, libfile)) {return; }}if (loader != null) { for (int i = 0 ; i < usr_paths.length ; i++) {File libfile = new File(usr_paths[i],System.mapLibraryName(name));if (loadLibrary0(fromClass, libfile)) { return;} }}// Oops, it failedthrow new UnsatisfiedLinkError('no ' + name + ' in java.library.path'); }LD_LIBRARY_PATH
為了搞清楚這個變量的作用,我們先說明一下Unix系統(tǒng)是如何加載動態(tài)庫的,然后自然就明白為什么要有LD_LIBRARY_PATH以及如何使用了。
動態(tài)庫如何加載?在基于GNU glibc的系統(tǒng)上,包括所有的linux系統(tǒng),啟動一個ELF格式的二進(jìn)制可執(zhí)行文件會自動調(diào)用加載器加載必要的動態(tài)鏈接庫,一個最簡單的可執(zhí)行文件一般也會包含一些系統(tǒng)的動態(tài)庫比如libc.so等。在Linux系統(tǒng)中,這個加載器叫做/lib/ld-linux.so.X,這個X指的是加載器的版本號。加載器然后查找并加載所需的動態(tài)庫。
加載器在什么路徑中搜索和加載動態(tài)庫呢——/etc/ld.so.conf,這個文件會包括/etc/ld.so.conf.d/*.conf這些文件夾中所有的.conf文件,而具體的動態(tài)庫搜索路徑,就包含在每個.conf文件中,比如/etc/ld.so.conf.d/libc.conf,它是libc的默認(rèn)的搜索路徑/usr/local/lib,這也是為什么我們不需要顯示聲明使用系統(tǒng)庫卻能自動完成加載的原因,也是為什么不同的系統(tǒng)編出來的庫無法通用的可見原因之一,因為不同系統(tǒng)的/usr/local/lib目錄下的動態(tài)庫并不一致。
如果每次啟動都去查找所有的目錄,那樣顯然是比較笨的做法,所以使用/etc/ld.so.cache來緩存路徑,并通過ldconfig來更新這個緩存路徑,有興趣的可以自行查看一下這個緩存文件。實際上,這個緩存路徑也很長了,基本上包含了系統(tǒng)可能存放動態(tài)庫的路徑。
為什么有LD_LIBRARY_PATH?上面我們說到可以通過cache和ldconfig來簡化搜索和加載動態(tài)庫的流程,但是還有兩個問題沒有考慮到,一是還沒有將編出來的庫放到系統(tǒng)目錄中去,二是依賴庫數(shù)量很少,不需要經(jīng)過這么復(fù)雜的查找。
LD_LIBRARY_PATH就是用來滿足這個需要,它也指定一個搜索路徑,且ld-linux.so會優(yōu)先在這個路徑下搜索需要的動態(tài)庫,如果沒找到,再去ld.so.conf中指定的目錄尋找。
使用export LD_LIBRARY_PATH=paths-to-libs
需要注意的一點是,多個目錄是通過:隔開的
區(qū)別前面分別介紹了java.library.path 和 LD_LIBRARY_PATH,都是為了加載所需的動態(tài)庫,有什么區(qū)別呢?
前者是在java環(huán)境中調(diào)用,在jvm啟動前設(shè)置生效;后者也是在啟動前,但是是在Unix環(huán)境中使用前者是通過修改property來設(shè)置路徑;后者是直接增加了ld-linux.so的搜索路徑對于JNI直接調(diào)用的庫,最好使用前者,對于有多重依賴關(guān)系的庫,最好使用LD_LIBRARY_PATH 參考HowTo: How to configure library path for JNI dependent librarieshttps://zauner.nllk.net/post/0013-jni-and-the-java-library-path/https://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getProperties?https://www.tutorialspoint.com/java/lang/runtime_loadlibrary.htmhttps://stackoverflow.com/questions/27945268/difference-between-using-java-library-path-and-ld-library-pathLinux關(guān)于動態(tài)庫的文檔
到此這篇關(guān)于解析java.library.path和LD_LIBRARY_PATH的介紹與區(qū)別的文章就介紹到這了,更多相關(guān)java.library.path和LD_LIBRARY_PATH內(nèi)容請搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!
相關(guān)文章:
1. Docker究竟是什么 為什么這么流行 它的優(yōu)點和缺陷有哪些?2. ASP.NET MVC實現(xiàn)城市或車型三級聯(lián)動3. 用python登錄帶弱圖片驗證碼的網(wǎng)站4. python軟件測試Jmeter性能測試JDBC Request(結(jié)合數(shù)據(jù)庫)的使用詳解5. .NET 6實現(xiàn)滑動驗證碼的示例詳解6. 如何使用瀏覽器擴展篡改網(wǎng)頁中的JS 文件7. Python使用Pyqt5實現(xiàn)簡易瀏覽器(最新版本測試過)8. 如何在.net6webapi中使用自動依賴注入9. Element ui tree(樹)實現(xiàn)父節(jié)點選中時子節(jié)點不選中父節(jié)點取消時子節(jié)點自動取消功能10. Java 二分查找算法的實現(xiàn)
