linux兩種庫:動態庫和靜態庫(共享庫)說明
linux下有兩種庫:動態庫和靜態庫(共享庫)
二者的不同點在於程式碼被載入的時刻不同。
靜態庫的程式碼在編譯過程中已經被載入可執行程式,因此體積比較大。
動態庫(共享庫)的程式碼在可執行程式執行時才載入記憶體,在編譯過程中僅簡單的引用,因此程式碼體積比較小。
不同的應用程式如果呼叫相同的庫,那麼在記憶體中只需要有一份該動態庫(共享庫)的例項。
靜態庫和動態庫的最大區別,靜態情況下,把庫直接載入到程式中,而動態庫連結的時候,它只是保留介面,將動態庫與程式程式碼獨立,這樣就可以提高程式碼的可複用度,和降低程式的耦合度。
靜態庫在程式編譯時會被連線到目的碼中,程式執行時將不再需要該靜態庫。
動態庫在程式編譯時並不會被連線到目的碼中,而是在程式執行是才被載入,因此在程式執行時還需要動態庫存在
一 靜態庫
這類庫的名字一般是libxxx.a;利用靜態函式庫編譯成的檔案比較大,因為整個 函式庫的所有資料都會被整合進目的碼中,他的優點就顯而易見了,即編譯後的執行程式不需要外部的函式庫支援,因為所有使用的函式都已經被編譯進去了。當然這也會成為他的缺點,因為如果靜態函式庫改變了,那麼你的程式必須重新編譯。
靜態庫的程式碼在編譯時連結到應用程式中,因此編譯時庫檔案必須存在,並且需要通過“-L”引數傳遞路徑給編譯器,應用程式在開始執行時,庫函式程式碼將隨程式一起調入程序記憶體段直到程序結束,其執行過程不需要原靜態庫存在。
在UNIX中,使用ar命令建立或者操作靜態庫
ar archivefile objfile
archivefile:archivefile是靜態庫的名稱
objfile:objfile是已.o為副檔名的中間目標檔名,可以多個並列
引數 意義
-r 將objfile檔案插入靜態庫尾或者替換靜態庫中同名檔案
-x 從靜態庫檔案中抽取檔案objfile
-t 列印靜態庫的成員檔案列表
-d 從靜態庫中刪除檔案objfile
-s 重置靜態庫檔案索引
-v 建立檔案冗餘資訊
-c 建立靜態庫檔案
example:
/******** hello.h ****/
void hello(void);
/******** hello.cpp ****/
include
include”hello.h”
using namespace std;
void hello(void)
{
cout <<"Hello "<<endl;
}
/******** main.cpp ****/
include”hello.h”
int main(int argc,char *argv[])
{
hello();
return 0;
}
1.編譯成靜態庫
無論靜態庫,還是動態庫,都是由.o檔案建立的。因此,我們必須將源程式hello.c通過gcc先編譯成.o檔案。
[email protected]:~/weiming/tt> g++ -o hello.o -c hello.cpp
[email protected]:~/weiming/tt> ar cqs libHello.a hello.o
[email protected]:~/weiming/tt> ls
hello.cpp hello.h hello.o libHello.a main.cpp
2.連結
[email protected]:~/weiming/tt> g++ main.cpp libHello.a -o Out1 (g++ -o aOut main.cpp ./libHello.a 或者 g++ -o aOut main.cpp -L./ -lHello)
注意:如果hello() 裡面還使用了其他的庫函式比如pthread_create,則最後生成Out1 時還需 -lpthread,但ar 時可以不用,只需要在 include 的標頭檔案中找到函式符號宣告即可,但最終生成可執行檔案時需要找到所有的符號定義。
[email protected]:~/weiming/tt> ls
hello.cpp hello.h hello.o libHello.a main.cpp Out1
[email protected]:~/weiming/tt> ldd Out1
linux-gate.so.1 => (0xffffe000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0xb7e36000)
libm.so.6 => /lib/libm.so.6 (0xb7e11000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0xb7e06000)
libc.so.6 => /lib/libc.so.6 (0xb7ce3000)
/lib/ld-linux.so.2 (0xb7f1b000)
二: 動態庫
這類庫的名字一般是libxxx.so;相對於靜態函式庫,動態函式庫在編譯的時候 並沒有被編譯進目的碼中,你的程式執行到相關函式時才呼叫該函式庫裡的相應函式,因此動態函式庫所產生的可執行檔案比較小。由於函式庫沒有被整合進你的程式,而是程式執行時動態的申請並呼叫,所以程式的執行環境中必須提供相應的庫。動態函式庫的改變並不影響你的程式,所以動態函式庫的升級比較方便
不同的UNIX系統,連結動態庫方法,實現細節不一樣
編譯PIC型.o中間檔案的方法一般是採用C語言編譯器的-KPIC或者-fpic選項,有的UNIX版本C語言編譯器預設帶上了PIC標準.建立最終動態庫的方法一般採用C語言編譯器的-G或者-shared選項,或者直接使用工具ld建立。
最主要的是GCC命令列的一個選項:
-shared 該選項指定生成動態連線庫(讓聯結器生成T型別的匯出符號表,有時候也生成弱連線W型別的匯出符號),不用該標誌外部程式無法連線。相當於一個可執行檔案
-fPIC:表示編譯為位置獨立的程式碼,不用此選項的話編譯後的程式碼是位置相關的所以動態載入時是通過程式碼拷貝的方式來滿足不同程序的需要,而不能達到真正程式碼段共享的目的。(轉者注:共享庫各段的載入地址並沒有定死,可以載入到任意位置,因為指令中沒有使用絕對地址(相對於連結後的可執行檔案各segment來說),因此稱為位置無關程式碼)
-L.:表示要連線的庫在當前目錄中
-ltest:編譯器查詢動態連線庫時有隱含的命名規則,即在給出的名字前面加上lib,後面加上.so來確定庫的名稱
這裡分別將原始檔d1.c和d2.c編譯為動態庫d1.so和d2.so.
/** d1.h***************/
void print();
/***** d1.cpp *********/
include
include “d1.h”
using namespace std
int p = 1;
void print()
{
cout<< p <<endl;
}
/** d2.h***************/
void print();
/***** d2.cpp *********/
include
include “d2.h”
using namespace std;
int p = 2;
void print()
{
cout<< p <
cp ./libd1.so libd.so (cp ./libd2.so libd.so )
g++ -o dOut main.cpp ./libd.so (或者g++ -o dOut main.cpp -L./ -ld)
[email protected]:~/weiming/tt/dd> ldd dOut
linux-gate.so.1 => (0xffffe000)
libd.so => ./libd.so (0xb7f0f000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0xb7e2b000)
libm.so.6 => /lib/libm.so.6 (0xb7e06000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0xb7dfa000)
libc.so.6 => /lib/libc.so.6 (0xb7cd8000)
/lib/ld-linux.so.2 (0xb7f12000)
ldd模擬執行一遍main,在執行過程中做動態連結,從而得知這個可執行檔案依賴於哪些共享庫,每個共享庫都在什麼路徑下,載入到程序地址空間的什麼地址。
總之,共享庫的搜尋路徑由動態連結器決定,從ld.so(8)的Man Page可以查到共享庫路徑的搜尋順序:
1. 首先在環境變數LD_LIBRARY_PATH所記錄的路徑中查詢。
在程式連結時指定的 rpath 中查詢,可以 readelf binfile | grep RPATH 。
然後從快取檔案/etc/ld.so.cache中查詢。這個快取檔案由/sbin/ldconfig命令讀取配置檔案/etc/ld.so.conf 之後生成。
(也可以在 ld.so.conf.d 目錄下增加 .conf 檔案,裡面寫入庫路徑,在 ld.so.conf 中 include ld.so.conf.d/.conf )
- 如果上述步驟都找不到,則到預設的系統路徑中查詢,先是/usr/lib然後是/lib。
不同的UNIX所依賴的動態庫查詢路徑環境變數名稱各不相同
UNIX版本 動態庫查詢路徑環境變數
AIX LIB_PATH
LINUX LD_LIBRARY_PATH
HP_UNIX PAHT
SCO UNIX LD_LIBRARY_PATH
動態連結庫取代靜態庫的好處之一就是可以隨時升級庫的內容。
當動態庫被介面完全相同的庫檔案取代後,可執行程式能迅速的切換到新動態庫中程式碼,省去了編譯的麻煩。
顯式呼叫動態庫
顯式呼叫動態庫,編譯時無需庫檔案,執行時動態可儲存於任意位置,庫裡共享物件必須先申請後使用,不同動態庫版本,只要其共享物件介面相同,就可以直接動態載入。注意新增 “-ldl” 編譯引數。
//開啟動態庫
#include<dlfcn.h>
void *dlopen(const char * pathname,int mode);
//獲取動態庫物件地址
include<dlfcn.h>
void *dlsym(void *handle,const char *name);
//錯誤檢測
include<dlfcn.h>
char *dlerror(vid);
//關閉動態庫
include<dlfcn.h>
int dlclose(void * handle);
動態庫的載入或多或少會佔用一定的系統資源,比如記憶體等。因此當不需要或者一段時間內不需要共享動態庫時就要解除安裝之。函式dlclose關閉引數handle所指向的動態庫,解除安裝其所佔的記憶體等資源,此呼叫後引數handle無效。
實際上,由於動態庫可能同時被多個程序共享,當一個程序指向dlclose時,資源並不馬上被解除安裝,只有當全部程序都宣佈關閉動態庫後,作業系統才開始回收動態庫資源。
總結:
編譯靜態庫時先使用-c選項,再利用ar工具產生.編譯動態庫的方式依不同版本的UNXI而定。隱式呼叫動態庫與靜態庫的用法相一致,而顯示呼叫動態庫則需要藉助動態載入共享庫函式族。
隱式呼叫動態庫和靜態庫使用方法一致,使用靜態庫和使用動態庫編譯成目標程式使用的gcc命令完全一樣,那當靜態庫和動態庫同名時,gcc命令會使用哪個庫檔案呢?
通過測試可以發現,當靜態庫和動態庫同名時, gcc命令將優先使用動態庫.為了確保使用的是靜態庫, 編譯時可以加上 -static 選項,因此多第三方程式為了確保在沒有相應動態庫時執行正常,喜歡在編譯最後應用程式時加入-static
相關推薦
linux兩種庫:動態庫和靜態庫(共享庫)說明
linux下有兩種庫:動態庫和靜態庫(共享庫) 二者的不同點在於程式碼被載入的時刻不同。 靜態庫的程式碼在編譯過程中已經被載入可執行程式,因此體積比較大。 動態庫(共享庫)的程式碼在可執行程式執行時才載入記憶體,在編譯過程中僅簡單的引用,因此程式碼體積比較
如何理解Linux下的動態庫概念,和靜態庫概念,通俗易懂的解釋如下:
動態庫和靜態庫都是一組函式集合,打包在一起供應用程式呼叫,區別是: 靜態庫名稱一般為xxx.a,在編譯時和應用程式連結在一起,這樣的應用程式佔用空間較大。 動態庫名稱一般為xxx.so,對於動態庫即可以在編譯時連結,也可以使用dlopen()/dlsy
java中呼叫本地動態連結庫(*.DLL)的兩種方式詳解和not found library、打包成jar,war包dll無法載入等等問題解決辦法
我們經常會遇到需要java呼叫c++的案例,這裡就java呼叫DLL本地動態連結庫兩種方式,和載入過程中遇到的問題進行詳細介紹 1、通過System.loadLibrary("dll名稱,不需要字尾名
g++ 編譯動態鏈接庫和靜態鏈接庫
dconf lin ldconfig 不想 名稱 如果 保存 path -fpic 現在我有hello1.cpp和hello2.cpp兩個文件,現在我要生成動態鏈接庫libhello.so和靜態鏈接庫libhello.a。以下為步驟: 1.生成動態鏈接庫: g++ -m32
【轉】gcc 編譯使用動態鏈接庫和靜態鏈接庫
避免 因此 -s 階段 cap etc 可執行 選項 而已 1 庫的分類 根據鏈接時期的不同,庫又有靜態庫和動態庫之分。 靜態庫是在鏈接階段被鏈接的(好像是廢話,但事實就是這樣),所以生成的可執行文件就不受庫的影響了,即使庫被刪除了,程序依然可以成功運行。 有別於靜態庫,動
GCC編譯過程與動態鏈接庫和靜態鏈接庫
elf格式 方式 通過 ifd lan 匯編語言 cpp wid 本質 1. 庫的介紹 庫是寫好的現有的,成熟的,可以復用的代碼。現實中每個程序都要依賴很多基礎的底層庫,不可能每個人的代碼都從零開始,因此庫的存在意義非同尋常。 本質上來說庫是一種可執行代碼的二進制形式,
《CMake實踐》筆記三:構建靜態庫(.a) 與 動態庫(.so) 及 如何使用外部共享庫和標頭檔案
五、靜態庫與動態庫構建 讀者雲,太能羅唆了,一個Hello World就折騰了兩個大節。OK,從本節開始,我們不再折騰Hello World了,我們來折騰Hello World的共享庫。 本節的任務: 1、建立一個靜態庫和動態庫,提供HelloFunc函式供其他程式程式設計使用,Hell
【轉】《CMake實踐》筆記三:構建靜態庫(.a) 與 動態庫(.so) 及 如何使用外部共享庫和標頭檔案
五、靜態庫與動態庫構建讀者雲,太能羅唆了,一個Hello World就折騰了兩個大節。OK,從本節開始,我們不再折騰Hello World了,我們來折騰Hello World的共享庫。本節的任務:1、建立一個靜態庫和動態庫,提供HelloFunc函式供其他程式程式設計使用,H
cmake中新增引用動態連結和靜態連結庫
動態庫的新增: link_directories(${PROJECT_SOURCE_DIR}/lib) #新增動態連線庫的路徑 target_link_libraries(project_name
手把手教你如何基於Anaconda安裝Tensorflow(Windows和Linux兩種版本)
現在越來越多的人工智慧和機器學習以及深度學習,強化學習出現了,然後自己也對這個產生了點興趣,特別的進行了一點點學習,就通過這篇文章來簡單介紹一下,關
linux下的兩種編輯器vi和vim的區別
vi vim是vi的加強版。語法加亮 vim 命令模式,命令列模式,編輯模式 vim 命令模式 字元操作: i 當前插入 I 行首插入 a 當前字元之後插入 A 行尾插入 o 下一行插入 O 上一
LINUX下兩種tar打包(.bz2)和(.gz)壓縮效率時間對比試驗
試驗檔案大小:204M,檔名:xx.dat 壓縮 1. tar czvf test.tar.gz xx.dat 耗時20秒,打包後大小:123M 2.tar cjvf test.tar.bz2 xx.dat 耗時82秒,打包後大小:133M 解壓 1. tar
手把手教你如何安裝Tensorflow(Windows和Linux兩種版本)
現在越來越多的人工智慧和機器學習以及深度學習,強化學習出現了,然後自己也對這個產生了點興趣,特別的進行了一點點學習,就通過這篇文章來簡單介紹一下,關於如何搭建Tensorflow以及如何進行使用。建議的話,還是要學習了一點Python基礎知識和Linux知識是最好的
使用JDK和Cglib兩種方式動態代理
一 使用JDK動態代理這種方式,只能對介面進行動態代理,有一定的侷限性;介面:package org.spring.test2; import java.util.Map; public interface UserService { void insert(Map&l
Linux 下操作gpio(兩種方法,驅動和mmap)
目前我所知道的在linux下操作GPIO有兩種方法: 1. 編寫驅動,這當然要熟悉linux下驅動的編寫方法和技巧,在驅動裡可以使用ioremap函式獲得GPIO物理基地址指標,然後使用這個指標根據ioctl命令進行GPIO暫存器的讀寫,並把結果回送到應用層。這裡提供
linux動態編譯和靜態編譯
為了使用方便,可以把這兩個函式介面定義為動態連結庫或靜態連結庫。用動態連結庫編譯生成的可執行檔案需呼叫.so檔案方可正常執行,靈活但稍顯麻煩;用靜態連結庫編譯生成的可執行檔案可直接執行,不用再呼叫如.so般的依賴庫檔案,簡單但不靈活。靜態連結庫:1、編譯生成目標檔案gcc
Spring中AOP實現的兩種方式之JDK和cglib的動態代理
AOP的實現原理: 都是基於代理模式,都是生成一個大代理物件 靜態AOP: AspectJ實現的AOP, 將切面程式碼直接編譯到Java類檔案中 --- 實現: JDK提供的動態代理技術 動態AOP: 將切面程式碼進行動態織入實現的AOP --- Spring的AOP為動態
linux下終端分屏使用的兩種方法(screen和tmux)
本文主要介紹兩種終端分屏工具:screen和tmux,分享出來供大家參考學習,下面來看看詳細的介紹: 一、使用screen分屏(只能上下分屏,不能左右分屏) (1)安裝工具 在ubuntu系統中使用sudo apt-get install screen 安裝s
關於CUDA兩種API:Runtime API 和 Driver API
ive uda ++ etime bsp con spa runt cuda CUDA 眼下有兩種不同的 API:Runtime API 和 Driver API,兩種 API 各有其適用的範圍。高級API(cuda_runtime.h)是一種C
動態網頁和靜態網頁的區別
get 工具 應用 間接 target 得到 代碼 讀取 發出 一、從功能方面來說動態網站與靜態網站的區別 1. 動態網站可以實現靜態網站所實現不了的功能,比方說:聊天室、論壇、音樂播放、瀏覽器、搜索等;而靜態的網站則實現不了。2. 靜態網站,如用Frontpage或Dre