日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux下静态库、动态库总结

發(fā)布時(shí)間:2024/1/8 linux 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux下静态库、动态库总结 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、基本概念

1.1、什么是庫

???????在 windows 平臺(tái)和 linux 平臺(tái)下都大量存在著庫。

?????? 本質(zhì)上來說庫是一種可執(zhí)行的二進(jìn)制代碼(但不可以獨(dú)立執(zhí)行),可以被操作系統(tǒng)載入內(nèi)存執(zhí)行

?????? 由于 windows 和 linux 的平臺(tái)不同(主要是編譯器、匯編器和連接器 的不同),因此二者庫的二進(jìn)制是不兼容的。

?????? 本文僅限于介紹 linux 下的庫。

1.2、?庫的種類

??????linux?下的庫有兩種:靜態(tài)庫共享庫(動(dòng)態(tài)庫)。

???二者的不同點(diǎn)在于代碼被載入的時(shí)刻不同:

???靜態(tài)庫的代碼在編譯過程中已經(jīng)被載入可執(zhí)行程序,因此生成的可執(zhí)行程序體積較大。靜態(tài)用.a為后綴, 例如: libhello.a

?? 共享庫(動(dòng)態(tài)庫)的代碼是在可執(zhí)行程序運(yùn)行時(shí)才載入內(nèi)存的,在編譯過程中僅簡單的引用,因此生成的可執(zhí)行程序代碼體積較小。

???動(dòng)態(tài)通常用.so為后綴, 例如:libhello.so

??????共享庫(動(dòng)態(tài)庫)的好處是::?不同的應(yīng)用程序如果調(diào)用相同的庫,那么在內(nèi)存里只需要有一份該共享庫的實(shí)例。

??????為了在同一系統(tǒng)中使用不同版本的庫,可以在庫文件名后加上版本號(hào)為后綴,例如: libhello.so.1.0,由于程序連接默認(rèn)以.so為文件后綴名。所以為了使用這些庫,通常使用建立符號(hào)連接的方式。

??????ln -s libhello.so.1.0 libhello.so.1?

????? ln -s libhello.so.1 libhello.so

1.3、靜態(tài)庫,動(dòng)態(tài)庫文件在linux下是如何生成的: 以下面的代碼為例,生成上面用到的hello庫: /* hello.c */ #include "hello.h" void sayhello() { printf("hello,world "); }

首先用gcc編繹該文件,在編繹時(shí)可以使用任何合法的編繹參數(shù),例如-g加入調(diào)試代碼等:

gcc -c hello.c -o hello.o

1、生成靜態(tài)庫 生成靜態(tài)庫使用ar工具,其實(shí)ar是archive的意思

ar cqs libhello.a hello.o

2、生成動(dòng)態(tài)庫 用gcc來完成,由于可能存在多個(gè)版本,因此通常指定版本號(hào):

gcc -shared -o libhello.so.1.0 hello.o?1.4、庫文件是如何命名的,有沒有什么規(guī)范: 在 linux 下,庫文件一般放在/usr/lib和/lib下, 靜態(tài)庫的名字一般為libxxxx.a,其中 xxxx 是該lib的名稱;動(dòng)態(tài)庫的名字一般為libxxxx.so.major.minor,xxxx 是該lib的名稱,major是主版本號(hào),minor是副版本號(hào)?1.5、可執(zhí)行程序在執(zhí)行的時(shí)候如何定位共享庫(動(dòng)態(tài)庫)文件 : 當(dāng)系統(tǒng)加載可執(zhí)行代碼(即庫文件)的時(shí)候,能夠知道其所依賴的庫的名字,但是還需要知道絕對(duì)路徑,此時(shí)就需要系統(tǒng)動(dòng)態(tài)載入器 (dynamic linker/loader) 對(duì)于 elf 格式的可執(zhí)行程序,是由 ld-linux.so* 來完成的,它先后搜索 elf 文件的 DT_RPATH 段-->環(huán)境變量LD_LIBRARY_PATH—->/etc/ld.so.cache 文件列表--> /lib/,/usr/lib 目錄找到庫文件后將其載入內(nèi)存 如: export LD_LIBRARY_PATH=’pwd’ 將當(dāng)前文件目錄添加為共享目錄。?1.6、使用ldd工具,查看可執(zhí)行程序依賴那些動(dòng)態(tài)庫或著動(dòng)態(tài)庫依賴于那些動(dòng)態(tài)庫 ldd 命令可以查看一個(gè)可執(zhí)行程序依賴的共享庫, 例如 # ldd /bin/lnlibc.so.6 => /lib/libc.so.6 (0×40021000)/lib/ld-linux.so.2 => /lib/ld- linux.so.2 (0×40000000) 可以看到 ln 命令依賴于 libc 庫和 ld-linux 庫 ?1.7、使用nm工具,查看靜態(tài)庫動(dòng)態(tài)庫有那些函數(shù)名; T類表示函數(shù)是當(dāng)前庫中定義的U類表示函數(shù)是被調(diào)用的,在其它庫中定義的W類當(dāng)前庫中定義,被其它庫中的函數(shù)覆蓋)。 有時(shí)候可能需要查看一個(gè)庫中到底有哪些函數(shù),nm工具可以打印出庫中的涉及到的所有符號(hào),這里的庫既可以是靜態(tài)的也可以是動(dòng)態(tài)的。

nm列出的符號(hào)有很多, 常見的有三種::

T類:庫中定義的函數(shù),用T表示,這是最常見的

U類:在庫中被調(diào)用,但并沒有在庫中定義(表明需要其他庫支持),用U表示

W類:是所謂的“弱態(tài)”符號(hào),它們雖然在庫中被定義,但是可能被其他庫中的同名符號(hào)覆蓋,用W表示

例如,假設(shè)開發(fā)者希望知道上文提到的hello庫中是否引用了 printf():

nm libhello.so | grep printf

發(fā)現(xiàn)printf是U類符號(hào),說明printf被引用,但是并沒有在庫中定義。

由此可以推斷,要正常使用hello庫,必須有其它庫支持,使用ldd工具查看hello依賴于哪些庫:

ldd libhello.so

libc.so.6=>/lib/libc.so.6(0x400la000)

/lib/ld-linux.so.2=>/lib/ld-linux.so.2 (0x40000000)

從上面的結(jié)果可以繼續(xù)查看printf最終在哪里被定義,有興趣可以go on

?

1.8、使用ar工具,可以生成靜態(tài)庫,同時(shí)可以查看靜態(tài)庫中包含那些.o文件,即有那些源文件構(gòu)成。

可以使用 ar -t libname.a 來查看一個(gè)靜態(tài)庫由那些.o文件構(gòu)成。

可以使用 ar q libname.a xxx1.o xxx2.o xxx3.o ... xxxn.o 生成靜態(tài)庫

?

1.9、如何查看動(dòng)態(tài)庫和靜態(tài)庫是32位,還是64位下的庫:

如果是動(dòng)態(tài)庫,可以使用file *.so;

如果是靜態(tài)哭,可以使用objdump -x *.a

?

Linux下進(jìn)行程序設(shè)計(jì)時(shí),關(guān)于庫的使用:一、gcc/g++命令中關(guān)于庫的參數(shù): -shared: 該選項(xiàng)指定生成動(dòng)態(tài)連接庫; -fPIC:表示編譯為位置獨(dú)立(地址無關(guān))的代碼,不用此選項(xiàng)的話,編譯后的代碼是位置相關(guān)的,所以動(dòng)態(tài)載入時(shí),是通過代碼拷貝的方式來滿足不同進(jìn)程的需要,而不能達(dá)到真正代碼段共享的目的。 -L:指定鏈接庫的路徑,-L. 表示要連接的庫在當(dāng)前目錄中 -ltest:指定鏈接庫的名稱為test,編譯器查找動(dòng)態(tài)連接庫時(shí)有隱含的命名規(guī)則,即在給出的名字前面加上lib,后面加上.so來確定庫的名稱 -Wl,-rpath: 記錄以來so文件的路徑信息。 LD_LIBRARY_PATH:這個(gè)環(huán)境變量指示動(dòng)態(tài)連接器可以裝載動(dòng)態(tài)庫的路徑。當(dāng)然如果有root權(quán)限的話,可以修改/etc/ld.so.conf文件,然后調(diào)用 /sbin/ldconfig來達(dá)到同樣的目的, 不過如果沒有root權(quán)限,那么只能采用修改LD_LIBRARY_PATH環(huán)境變量的方法了。 調(diào)用動(dòng)態(tài)庫的時(shí)候,有幾個(gè)問題會(huì)經(jīng)常碰到: 1、有時(shí),明明已經(jīng)將庫的頭文件所在目錄 通過 “-I” include進(jìn)來了,庫所在文件通過 “-L”參數(shù)引導(dǎo),并指定了“-l”的庫名,但通過ldd命令察看時(shí),就是死活找不到你指定鏈接的so文件,這時(shí)你要作的就是通過修改 LD_LIBRARY_PATH或者/etc/ld.so.conf文件來指定動(dòng)態(tài)庫的目錄。通常這樣做就可以解決庫無法鏈接的問題了。?二、靜態(tài)庫鏈接時(shí)搜索路徑的順序: 1. ld會(huì)去找gcc/g++命令中的參數(shù)-L;2. 再找gcc的環(huán)境變量LIBRARY_PATH,它指定程序靜態(tài)鏈接庫文件搜索路徑; export LIBRARY_PATH=$LIBRARY_PATH:data/home/billchen/lib 3. 再找默認(rèn)庫目錄 /lib /usr/lib /usr/local/lib,這是當(dāng)初compile gcc時(shí)寫在程序內(nèi)的。 ?三、動(dòng)態(tài)鏈接時(shí)、執(zhí)行時(shí)搜索路徑順序: 1. 編譯目標(biāo)代碼時(shí)指定的動(dòng)態(tài)庫搜索路徑; 2. 環(huán)境變量LD_LIBRARY_PATH指定動(dòng)態(tài)庫搜索路徑,它指定程序動(dòng)態(tài)鏈接庫文件搜索路徑; export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:data/home/billchen/lib 3. 配置文件/etc/ld.so.conf中指定的動(dòng)態(tài)庫搜索路徑;4. 默認(rèn)的動(dòng)態(tài)庫搜索路徑/lib;5. 默認(rèn)的動(dòng)態(tài)庫搜索路徑/usr/lib。 ?四、靜態(tài)庫和動(dòng)態(tài)鏈接庫同時(shí)存在時(shí),gcc/g++默認(rèn)鏈接的是動(dòng)態(tài)庫: 當(dāng)一個(gè)庫同時(shí)存在靜態(tài)庫和動(dòng)態(tài)庫時(shí),比如libmysqlclient.a和libmysqlclient.so同時(shí)存在時(shí): 在Linux下,動(dòng)態(tài)庫和靜態(tài)庫同事存在時(shí),gcc/g++的鏈接程序,默認(rèn)鏈接的動(dòng)態(tài)庫。 可以使用下面的方法,給連接器ld傳遞參數(shù),看是否鏈接動(dòng)態(tài)庫還是靜態(tài)庫。 -Wl,-Bstatic -llibname //指定讓gcc/g++鏈接靜態(tài)庫 使用: gcc/g++ test.c -o test -Wl,-Bstatic -llibname -Wl,-Bdynamic -lm -lc -Wl,-Bdynamic -llibname //指定讓gcc/g++鏈接動(dòng)態(tài)庫 使用: gcc/g++ test.c -o test -Wl,-Bdynamic -llibname 如果要完全靜態(tài)加在,使用-static參數(shù),即將所有的庫以靜態(tài)的方式鏈入可執(zhí)行程序,這樣生成的可執(zhí)行程序,不再依賴任何庫,同事出現(xiàn)的問題是,這樣編譯出來的程序非常大,占用空間。如果不適用-Wl,-Bdynamic -lm -c會(huì)有如下錯(cuò)誤:[chenbaihu@build17 lib]$ ls libtest.a libtest.so t t.cc test.cc test.h test.o [chenbaihu@build17 lib]$ g++ -Wall -g t.cc -o t -L./ -Wl,-Bstatic -ltest -Wl,-Bdynamic -lm -lc [chenbaihu@build17 lib]$ g++ -Wall -g t.cc -o t -L./ -Wl,-Bstatic -ltest /usr/bin/ld: cannot find -lm collect2: ld 返回 1參考: http://lists.gnu.org/archive/html/help-gnu-utils/2004-03/msg00009.html
五、有關(guān)環(huán)境變量: LIBRARY_PATH環(huán)境變量:指定程序靜態(tài)鏈接庫文件搜索路徑LD_LIBRARY_PATH環(huán)境變量:指定程序動(dòng)態(tài)鏈接庫文件搜索路徑 ?六、庫的依賴問題: 比如我們有一個(gè)基礎(chǔ)庫libbase.a,還有一個(gè)依賴libbase.a編譯的庫,叫做libchild.a;在我們編譯程序時(shí),一定要先-lchild再-lbase。?如果使用 -lbase -lchild,在編譯時(shí)將出現(xiàn)一些函數(shù)undefined,而這些函數(shù)實(shí)際上已經(jīng)在base中已經(jīng)定義; 為什么會(huì)有庫的依賴問題? 一、靜態(tài)庫解析符號(hào)引用:鏈接器ld是如何使用靜態(tài)庫來解析引用的。在符號(hào)解析階段,鏈接器從左至右,依次掃描可重定位目標(biāo)文件(*.o)靜態(tài)庫(*.a)。在這個(gè)過程中,鏈接器將維持三個(gè)集合: 集合E:可重定位目標(biāo)文件(*.o文件)的集合。集合U:未解析(未定義)的符號(hào)集,即符號(hào)表中UNDEF的符號(hào)。集合D: 已定義的符號(hào)集。初始情況下,E、U、D均為空。1、對(duì)于每個(gè)輸入文件f,如果是目標(biāo)文件(.o),則將f加入E,并用f中的符號(hào)表修改U、D(在文件f中定義實(shí)現(xiàn)的符號(hào)是D,在f中引用的符號(hào)是U),然后繼續(xù)下個(gè)文件。2、如果f是一個(gè)靜態(tài)庫(.a),那么鏈接器將嘗試匹配U中未解析符號(hào)與靜態(tài)庫成員(靜態(tài)庫的成員就是.o文件)定義的符號(hào)。如果靜態(tài)庫中某個(gè)成員m(某個(gè).o文件)定義了一個(gè)符號(hào)來解析U中引用,那么將m加入E中,同時(shí)使用m的符號(hào)表,來更新U、D。對(duì)靜態(tài)庫中所有成員目標(biāo)文件反復(fù)進(jìn)行該過程,直至U和D不再發(fā)生變化。此時(shí),靜態(tài)庫f中任何不包含在E中的成員目標(biāo)文件都將丟棄,鏈接器將繼續(xù)下一個(gè)文件。3、當(dāng)所有輸入文件完成后,如果U非空,鏈接器則會(huì)報(bào)錯(cuò),否則合并和重定位E中目標(biāo)文件,構(gòu)建出可執(zhí)行文件。 到這里,為什么會(huì)有庫的依賴問題已經(jīng)得到解答: ?因?yàn)閘ibchild.a依賴于libbase.a,但是libbase.a在libchild.a的左邊,導(dǎo)致libbase.a中的目標(biāo)文件(*.o)根本就沒有被加載到E中,所以解決方法就是交換兩者的順序。當(dāng)然也可以使用-lbase -lchild -lbase的方法。參考文章:http://pananq.com/index.php/page/3/?七、動(dòng)態(tài)庫升級(jí)問題: 在動(dòng)態(tài)鏈接庫升級(jí)時(shí),不能使用cp newlib.so oldlib.so,這樣有可能會(huì)使程序core掉;而應(yīng)該使用:rm oldlib.so 然后 cp newlib.so oldlib.so或者mv oldlib.so oldlib.so_bak 然后 cp newlib.so oldlib.so

為什么不能用cp newlib.so oldlib.so ?

在替換so文件時(shí),如果在不停程序的情況下,直接用 cp new.so old.so 的方式替換程序使用的動(dòng)態(tài)庫文件會(huì)導(dǎo)致正在運(yùn)行中的程序崩潰。

解決方法:

解決的辦法是采用“rm+cp” 或“mv+cp” 來替代直接“cp” 的操作方法。

linux系統(tǒng)的動(dòng)態(tài)庫有兩種使用方法:運(yùn)行時(shí)動(dòng)態(tài)鏈接庫,動(dòng)態(tài)加載庫并在程序控制之下使用。

1、為什么在不停程序的情況下,直接用 cp 命令替換程序使用的 so 文件,會(huì)使程序崩潰? 很多同學(xué)在工作中遇到過這樣一個(gè)問題,在替換 so 文件時(shí),如果在不停程序的情況下,直接用cp new.so old.so的方式替換程序使用的動(dòng)態(tài)庫文件會(huì)導(dǎo)致正在運(yùn)行中的程序崩潰,退出。

這與 cp 命令的實(shí)現(xiàn)有關(guān),cp 并不改變目標(biāo)文件的 inode,cp 的目標(biāo)文件會(huì)繼承被覆蓋文件的屬性而非源文件。實(shí)際上它是這樣實(shí)現(xiàn)的: strace cp libnew.so libold.so 2>&1 |grep open.*lib.*.so open("libnew.so", O_RDONLY|O_LARGEFILE) = 3 open("libold.so", O_WRONLY|O_TRUNC|O_LARGEFILE) = 4 在 cp 使用“O_WRONLY|O_TRUNC” 打開目標(biāo)文件時(shí),原 so 文件的鏡像被意外的破壞了。這樣動(dòng)態(tài)鏈接器 ld.so 不能訪問到 so 文件中的函數(shù)入口。從而導(dǎo)致 Segmentation fault,程序崩潰。ld.so 加載 so 文件及“再定位”的機(jī)制比較復(fù)雜。

2、怎樣在不停止程序的情況下替換so文件,并且保證程序不會(huì)崩潰? 答案是采用“rm+cp” 或“mv+cp” 來替代直接“cp” 的操作方法。

在用新的so文件 libnew.so 替換舊的so文件 libold.so 時(shí),如果采用如下方法:rm libold.so //如果內(nèi)核正在使用libold.so,那么inode節(jié)點(diǎn)不會(huì)立刻別刪除掉。 cp libnew.so libold.so采用這種方法,目標(biāo)文件 libold.so 的 inode 其實(shí)已經(jīng)改變了,原來的 libold.so 文件雖然不能用"ls"查看到,但其inode并沒有被真正刪除,直到內(nèi)核釋放對(duì)它的引用。

(即: rm libold.so,此時(shí),如果ld.so正在加在libold.so,內(nèi)核就在引用libold.so的inode節(jié)點(diǎn),rm libold.so的inode并沒有被真正刪除,當(dāng)ld.so對(duì)libold.so的引用結(jié)束,inode才會(huì)真正刪除。這樣程序就不會(huì)崩潰,因?yàn)樗€在使用舊的libold.so,當(dāng)下次再使用libold.so時(shí),已經(jīng)被替換,就會(huì)使用新的libold.so)

同理,mv只是改變了文件名,其 inode 不變,新文件使用了新的 inode。這樣動(dòng)態(tài)鏈接器 ld.so 仍然使用原來文件的 inode 訪問舊的 so 文件。因而程序依然能正常運(yùn)行。

(即: mv libold.so ***后,如果程序使用動(dòng)態(tài)庫,還是使用舊的inode節(jié)點(diǎn),當(dāng)下次再使用libold.so時(shí),就會(huì)使用新的libold.so)

到這里,為什么直接使用“cp new_exec_file old_exec_file”這樣的命令時(shí),系統(tǒng)會(huì)禁止這樣的操作,并且給出這樣的提示“cp: cannot create regular file `old': Text file busy”。

這時(shí),我們采用的辦法仍然是用“rm+cp”或者“mv+cp”來替代直接“cp”,這跟以上提到的so文件的替換有同樣的道理。

但是,為什么系統(tǒng)會(huì)阻止cp覆蓋可執(zhí)行程序,而不阻止覆蓋so文件

這是因?yàn)?Linux 有個(gè) Demand Paging 機(jī)制,所謂“Demand Paging”,簡單的說,就是系統(tǒng)為了節(jié)約物理內(nèi)存開銷,并不會(huì)程序運(yùn)行時(shí)就將所有頁(page)都加載到內(nèi)存中,而只有在系統(tǒng)有訪問需求時(shí)才將其加載。“Demand Paging”要求正在運(yùn)行中的程序鏡像注意,并非文件本身不被意外修改因此內(nèi)核在啟動(dòng)程序后會(huì)鎖定這個(gè)程序鏡像的 inode

對(duì)于 so 文件,它是靠 ld.so 加載的,而ld.so畢竟也是用戶態(tài)程序,沒有權(quán)利去鎖定inode,也不應(yīng)與內(nèi)核的文件系統(tǒng)底層實(shí)現(xiàn)耦合。

總結(jié)

以上是生活随笔為你收集整理的linux下静态库、动态库总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。