- 資訊首頁(yè) > 開(kāi)發(fā)技術(shù) >
- 解析java.library.path和LD_LIBRARY_PATH的介紹與區別
近期要將算法部署到一個(gè)機群的虛擬主機(Debian 9.1 gcc 6.3.0)上,采用的是Java + JNI + shared library的方式來(lái)完成底層算法能力的部署。
其中需要用到各種第三方庫,有從源碼編譯的,也有直接下載的so,包括OpenCV相關(guān)、TensorFlow相關(guān)、MKL以OpenMP相關(guān)的動(dòng)態(tài)庫。
遇到一個(gè)問(wèn)題,libmklml_intel.so 這個(gè)庫只能放在 LD_LIBRARY_PATH中進(jìn)行加載,而不能通過(guò)java.library.path完成加載,所以有必要搞清楚這兩個(gè)路徑究竟有什么區別。
的定義是:List of paths to search when loading libraries
從定義我們可以發(fā)現,首先是一個(gè)list,也就是說(shuō)可以包括多個(gè)地址,然后這些地址是用來(lái)幫助jvm搜索需要加載的庫文件的。
最簡(jiǎn)單的辦法就是在啟動(dòng)jvm前通過(guò)java -Djava.library.path=path-to-your-libs
設置這個(gè)全局變量。
那么這個(gè)地址具體是如何被使用的呢?
當我們調用System.loadLibrary(libname)
時(shí),會(huì )調用Runtime.loadLibary
,然后調用java/lang/ClassLoader.loadLibrary
。在ClassLoader.loadLibrary中,系統屬性java.library.path
將會(huì )被獲取,并用來(lái)生成需要加載的庫的絕對路徑,然后將這個(gè)絕對路徑傳給本地方法來(lái)調用dlopen/dlsym
并最終加載這個(gè)庫。
如果加載失敗,會(huì )根據實(shí)際情況返回三個(gè)異常值:
SecurityException − if a security manager exists and its checkLink method doesn't allow loading of the specified dynamic library
UnsatisfiedLinkError − if the library does not exist
NullPointerException − if libname is null
可以參考
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 failed throw new UnsatisfiedLinkError("no " + name + " in java.library.path"); }
為了搞清楚這個(gè)變量的作用,我們先說(shuō)明一下Unix系統是如何加載動(dòng)態(tài)庫的,然后自然就明白為什么要有LD_LIBRARY_PATH以及如何使用了。
在基于GNU glibc的系統上,包括所有的linux系統,啟動(dòng)一個(gè)ELF格式的二進(jìn)制可執行文件會(huì )自動(dòng)調用加載器加載必要的動(dòng)態(tài)鏈接庫,一個(gè)最簡(jiǎn)單的可執行文件一般也會(huì )包含一些系統的動(dòng)態(tài)庫比如libc.so等。在Linux系統中,這個(gè)加載器叫做/lib/ld-linux.so.X
,這個(gè)X指的是加載器的版本號。加載器然后查找并加載所需的動(dòng)態(tài)庫。
加載器在什么路徑中搜索和加載動(dòng)態(tài)庫呢——/etc/ld.so.conf
,這個(gè)文件會(huì )包括/etc/ld.so.conf.d/*.conf
這些文件夾中所有的.conf文件,而具體的動(dòng)態(tài)庫搜索路徑,就包含在每個(gè).conf文件中,比如/etc/ld.so.conf.d/libc.conf
,它是libc的默認的搜索路徑/usr/local/lib
,這也是為什么我們不需要顯示聲明使用系統庫卻能自動(dòng)完成加載的原因,也是為什么不同的系統編出來(lái)的庫無(wú)法通用的可見(jiàn)原因之一,因為不同系統的/usr/local/lib
目錄下的動(dòng)態(tài)庫并不一致。
如果每次啟動(dòng)都去查找所有的目錄,那樣顯然是比較笨的做法,所以使用/etc/ld.so.cache
來(lái)緩存路徑,并通過(guò)ldconfig來(lái)更新這個(gè)緩存路徑,有興趣的可以自行查看一下這個(gè)緩存文件。實(shí)際上,這個(gè)緩存路徑也很長(cháng)了,基本上包含了系統可能存放動(dòng)態(tài)庫的路徑。
上面我們說(shuō)到可以通過(guò)cache和ldconfig來(lái)簡(jiǎn)化搜索和加載動(dòng)態(tài)庫的流程,但是還有兩個(gè)問(wèn)題沒(méi)有考慮到,一是還沒(méi)有將編出來(lái)的庫放到系統目錄中去,二是依賴(lài)庫數量很少,不需要經(jīng)過(guò)這么復雜的查找。
LD_LIBRARY_PATH
就是用來(lái)滿(mǎn)足這個(gè)需要,它也指定一個(gè)搜索路徑,且ld-linux.so會(huì )優(yōu)先在這個(gè)路徑下搜索需要的動(dòng)態(tài)庫,如果沒(méi)找到,再去ld.so.conf中指定的目錄尋找。
export LD_LIBRARY_PATH=paths-to-libs
需要注意的一點(diǎn)是,多個(gè)目錄是通過(guò)
:
隔開(kāi)的
前面分別介紹了java.library.path 和 LD_LIBRARY_PATH,都是為了加載所需的動(dòng)態(tài)庫,有什么區別呢?
前者是在java環(huán)境中調用,在jvm啟動(dòng)前設置生效;后者也是在啟動(dòng)前,但是是在Unix環(huán)境中使用前者是通過(guò)修改property來(lái)設置路徑;后者是直接增加了ld-linux.so的搜索路徑對于JNI直接調用的庫,最好使用前者,對于有多重依賴(lài)關(guān)系的庫,最好使用LD_LIBRARY_PATH
HowTo: How to configure library path for JNI dependent libraries
https://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.htm
https://stackoverflow.com/questions/27945268/difference-between-using-java-library-path-and-ld-library-path
Linux關(guān)于動(dòng)態(tài)庫的文檔
到此這篇關(guān)于解析java.library.path和LD_LIBRARY_PATH的介紹與區別的文章就介紹到這了,更多相關(guān)java.library.path和LD_LIBRARY_PATH內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。
Copyright ? 2009-2021 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 珠海市特網(wǎng)科技有限公司 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站