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






