大家好,我是阿木實(shí)驗(yàn)室的松溪,今天給大家介紹一下,linux系統(tǒng)中,庫(kù)的概念。
一、庫(kù)的簡(jiǎn)介
什么是庫(kù),庫(kù)簡(jiǎn)單地說,就是模塊。用于提供不同功能的模塊,比如我們經(jīng)常會(huì)用的ceres庫(kù),eigen庫(kù),pcl庫(kù)等等。庫(kù)從本質(zhì)上來說,是一種可以執(zhí)行代碼的二進(jìn)制形式,可以被載入到內(nèi)存中使用,在Liunx系統(tǒng)中,庫(kù)以文件的形式存在,并且可以分為動(dòng)態(tài)鏈接庫(kù)和靜態(tài)鏈接庫(kù)兩種,簡(jiǎn)稱為動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù)。其中,靜態(tài)庫(kù)文件的后綴為.a,動(dòng)態(tài)庫(kù)文件的后綴為.so。無論是動(dòng)態(tài)庫(kù)還是靜態(tài)庫(kù),它們無非是向調(diào)用的人提供變量、函數(shù)或者類。
二、庫(kù)的區(qū)別
靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)的區(qū)別在于:二者代碼被載入的時(shí)刻不同。靜態(tài)庫(kù)在程序編譯的時(shí)候會(huì)被鏈接到目標(biāo)代碼中,目標(biāo)程序運(yùn)行的時(shí)候?qū)⒉辉傩枰搸?kù),移植方便,但是體積會(huì)變大,浪費(fèi)空間和資源,因?yàn)榕c之相關(guān)的所有文件都會(huì)鏈接合成一個(gè)可執(zhí)行文件,導(dǎo)致可執(zhí)行文件的體積變大。動(dòng)態(tài)庫(kù)在程序編譯的時(shí)候并不會(huì)被鏈接到目標(biāo)代碼中,而是在程序運(yùn)行的時(shí)候才被載入,因此可執(zhí)行文件體積較小。
基本上,大部分的庫(kù)都是動(dòng)態(tài)庫(kù),比如:eigen,ceres等,這就是我們通常所說的依賴項(xiàng),如果你的程序中有需要某個(gè)庫(kù),而你的環(huán)境中沒有這樣的庫(kù),那么在編譯的時(shí)候就會(huì)出現(xiàn)這樣的錯(cuò)誤:xxxx.h文件未找到。這就是典型的缺少動(dòng)態(tài)庫(kù)的bug。對(duì)此,只需要安裝對(duì)應(yīng)的動(dòng)態(tài)庫(kù)即可解決。這個(gè)問題經(jīng)常出現(xiàn)在當(dāng)你clone某一個(gè)代碼下來,但是卻沒有這個(gè)代碼的運(yùn)行環(huán)境,當(dāng)你編譯的時(shí)候,就會(huì)出現(xiàn)上面的報(bào)錯(cuò)。是不是又get到了一個(gè)小技巧呢?
三、靜態(tài)庫(kù)的創(chuàng)建和使用
在Linux系統(tǒng)中創(chuàng)建靜態(tài)庫(kù)的過程如下:
1. 編輯源文件(.c或.cpp文件)
2. 通過 gcc -c xxx.c 或 g++ -c xxx.cpp 生成目標(biāo)文件(.o文件)
3. 使用 ar 命令歸檔目標(biāo)文件,生成靜態(tài)庫(kù)
4. 配合靜態(tài)庫(kù)寫一個(gè)頭文件,文件里的內(nèi)容就是提供給調(diào)用者使用的函數(shù)、變量或者類的聲明。
在實(shí)際創(chuàng)建之前,有必要了解一下,ar命令的使用。ar命令不但可以創(chuàng)建靜態(tài)庫(kù),也可以修改或提取已有靜態(tài)庫(kù)中的信息,其基本用法如下:
ar [option] libxxx.a xx1.o xx2.o xx3.o
其中,libxxx.a是生成靜態(tài)庫(kù)的文件名字,xxx是自己設(shè)定的名稱,lib表示該文件是一個(gè)庫(kù),所有在Linux下的庫(kù),都遵守這種命名。即libxxx.a或者libxxx.so。xx1.o xx2.o為靜態(tài)庫(kù)的目標(biāo)代碼文件,可以有多個(gè)。option常見的選項(xiàng)如下:
-c:創(chuàng)建一個(gè)庫(kù),無論庫(kù)是否存在,都會(huì)創(chuàng)建
-s:創(chuàng)建目標(biāo)文件索引
-r:在庫(kù)中插入模塊,如插入的模塊名已經(jīng)在庫(kù)中存在,則將會(huì)替換。如果有一個(gè)模塊不存在,將會(huì)保存,并不會(huì)替換其他同名模塊
-t:顯示庫(kù)文件中有哪些目標(biāo)文件。僅僅顯示
-tv:顯示庫(kù)文件有哪些目標(biāo)文件。包括文件名、時(shí)間、大小等
-s:顯示靜態(tài)庫(kù)文件中的索引表
下面將講解如何創(chuàng)建和使用一個(gè)靜態(tài)庫(kù)
新建一個(gè)源文件,test.cpp,復(fù)制以下代碼:
#include <IOStream>
void show_age(int age) {
std::cout << "Your age is: " << age << std::endl;
}
然后輸入命令
g++ -c test.cpp
ar rcs libshowage.a test.o
這樣就生成了一個(gè)靜態(tài)庫(kù),要想使用,需要編寫另一個(gè)源文件,demo.cpp。復(fù)制以下代碼
#include <iostream>
extern void show_age(int age); //聲明要使用的函數(shù)
int main(int argc, char** argv) {
show_age(18);
std::cout << "Hi" << std::endl;
}
CMakeLists.txt文件
cmake_minimum_required(VERSION 3.0)
project(demo)
add_executable(demo demo.cpp)
target_link_libraries(demo -L.. -lshowage)
其中,-L參數(shù)表示從什么地方找這個(gè)庫(kù),(..)表示上一級(jí)目錄。-l指定具體的庫(kù),其中l(wèi)ib和.a不需要顯示寫出,編譯器會(huì)自動(dòng)去尋找libshowage.a這個(gè)文件。這也就是為什么庫(kù)命名的時(shí)候要以lib開頭。
四、動(dòng)態(tài)庫(kù)的創(chuàng)建和使用
在Linux系統(tǒng)中,存放動(dòng)態(tài)庫(kù)的路徑一般為/usr/lib。在Linux系統(tǒng)下進(jìn)行的鏈接,默認(rèn)是先鏈接動(dòng)態(tài)庫(kù),如果同時(shí)存在靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù),如果不特別指出,將與動(dòng)態(tài)庫(kù)鏈接。這樣有助于節(jié)省空間。
同理,我們創(chuàng)建一個(gè)動(dòng)態(tài)庫(kù),test.cpp。代碼同上,不做改變。只需要修改生成動(dòng)態(tài)庫(kù)的命令。該命令如下:
g++ test.cpp -fPIC -shared -o libshowage.so
-shared 表示生成共享庫(kù)
-fPIC表明使用地址無關(guān)代碼。PIC全稱是Position Independent Code。在Linux系統(tǒng)下編譯共享庫(kù)時(shí),必須加上這個(gè)參數(shù),否則在鏈接的時(shí)候?qū)?huì)報(bào)錯(cuò)。因?yàn)楣蚕韼?kù)文件可能會(huì)被不同的進(jìn)程加載到不同的位置上,如果共享對(duì)象中的指令使用了絕對(duì)地址、外部模塊地址等,那么該庫(kù)在被加載的時(shí)候,就需要修改地址,這樣就不能實(shí)現(xiàn)多進(jìn)程共享一份物理內(nèi)存。
編寫一個(gè)調(diào)用該庫(kù)的函數(shù),demo.cpp。內(nèi)容還是不變。CMakeLists.txt也不變。
五、多個(gè)文件生成一個(gè)動(dòng)態(tài)庫(kù)
創(chuàng)建test1.cpp文件,復(fù)制以下代碼
#include <iostream>
void show_age_1(int age) {
std::cout << "This is lib_1: " << age << std::endl;
}
創(chuàng)建test2.cpp文件,復(fù)制以下代碼
#include <iostream>
void show_age_2(int age) {
std::cout << "This is lib_2: " << age << std::endl;
}
創(chuàng)建動(dòng)態(tài)庫(kù)
g++ test1.cpp test2.cpp -fPIC -shared -o libshowage.so
需要說明的是,多個(gè)文件創(chuàng)建動(dòng)態(tài)庫(kù)的時(shí)候,不能有同名的函數(shù)、類、變量的聲明,否則將無法創(chuàng)建,因?yàn)榫退銊?chuàng)建成功以后,當(dāng)調(diào)用的時(shí)候,編譯器并不知道該去調(diào)用哪一個(gè)源文件里面的聲明。
創(chuàng)建 main.cpp 文件,使用這個(gè)庫(kù),復(fù)制以下代碼
#include <iostream>
//聲明要使用的函數(shù)
extern void show_age_1(int age);
extern void show_age_2(int age);
int main(int argc, char** argv) {
show_age_1(18);
show_age_2(36);
}
CMakeLists.txt文件如下
cmake_minimum_required(VERSION 3.0)
project(demo)
add_executable(main main.cpp)
target_link_libraries(main -L.. -lshowage)
當(dāng)我們運(yùn)行的時(shí)候,會(huì)發(fā)現(xiàn)這樣的錯(cuò)誤
./main: error while loading shared libraries: libshowage.so: cannot open shared object file: No such file or directory
在編譯的時(shí)候,我們告訴了編譯器,在什么地方去尋找該庫(kù),但是在運(yùn)行的時(shí)候,由于該庫(kù)是動(dòng)態(tài)庫(kù),其代碼沒有被鏈接到目標(biāo)代碼中,因此,在運(yùn)行的時(shí)候,會(huì)提示找不到該庫(kù)。
因此,只需要將動(dòng)態(tài)庫(kù)放到默認(rèn)的路徑上或者告訴其指定路徑即可。
在Linux系統(tǒng)中,一般將庫(kù)放在以下三個(gè)地方。其中,/lib用于放置系統(tǒng)級(jí)別(或者說內(nèi)核級(jí)別)的庫(kù)文件,/usr/lib 用于放置程序級(jí)別的庫(kù)文件,/usr/local/lib 為用戶級(jí)別,一般用戶編譯的都放在這里,以及源碼編譯安裝,例如:opencv的源碼編譯。
因此,我們要將自己的庫(kù)放在 /usr/local/lib 下,輸入以下命令
sudo cp ~/C++/demo/libshowage.so /usr/local/lib
當(dāng)我們移動(dòng)以后,需要輸入以下命令(必須),更新
sudo ldconfig
這樣,當(dāng)我們?cè)俅芜\(yùn)行的時(shí)候,就不會(huì)報(bào)錯(cuò)了。切記,每次有動(dòng)態(tài)庫(kù)在上述目錄中發(fā)生變動(dòng),都需要輸入這個(gè)命令更新,否則還是無法識(shí)別。
六、通過apt安裝缺少的動(dòng)態(tài)庫(kù)
當(dāng)我們從github上clone代碼的時(shí)候,在編譯的時(shí)候,經(jīng)常出現(xiàn)xxx.h文件不存在,或者No such file or directory。其本質(zhì)上就是你的環(huán)境和別人的環(huán)境不一致,缺少相應(yīng)的庫(kù)。一般的解決方式是,將該缺少的頭文件復(fù)制到百度,查一查這個(gè)頭文件是來自哪一個(gè)庫(kù)。然后打開終端,輸入命令
sudo apt install libxxx-dev
即可
如果是自定義的動(dòng)態(tài)庫(kù),一般在readme文檔中有說明。
同理,當(dāng)你向開源社區(qū)貢獻(xiàn)開源代碼的同時(shí),也請(qǐng)務(wù)必告知運(yùn)行的環(huán)境以及所需要的庫(kù)。
七、總結(jié)
相信通過本文的介紹,讀者對(duì)庫(kù)有了一個(gè)簡(jiǎn)單的認(rèn)識(shí),在以后的工作和學(xué)習(xí)過程中,遇到類似的問題,也有了解決問題的思路和方法。
阿木實(shí)驗(yàn)室致力于為機(jī)器人研發(fā)提供開源軟硬件工具和課程服務(wù),讓研發(fā)更高效!
- End -
技術(shù)發(fā)展的日新月異,阿木實(shí)驗(yàn)室將緊跟技術(shù)的腳步,不斷把機(jī)器人行業(yè)最新的技術(shù)和硬件推薦給大家。看到經(jīng)過我們培訓(xùn)的學(xué)員在技術(shù)上突飛猛進(jìn),是我們培訓(xùn)最大的價(jià)值。如果你在機(jī)器人行業(yè),就請(qǐng)關(guān)注我們的公眾號(hào),我們將持續(xù)發(fā)布機(jī)器人行業(yè)最有價(jià)值的信息和技術(shù)。