天天看點

我眼中的linux共享庫

共享庫數量龐大,是以作業系統需要對共享對象的目錄組織和使用方法有一定的規則。

1.1共享庫相容性

共享庫更新分為相容性更新和不相容性更新。共享庫中的接口,被稱作二進制接口,即ABI(application binary interface);

防止共享庫不相容的重要因素:不要改變接口的任何部分或者幹脆不要使用C++作為共享庫接口。

拓展:C++類模闆和模闆類

模闆類是類模闆執行個體化後的一個産物,說個具體點的例子吧,我們把類模闆比作是一個做餅幹的模子,而模闆類就是用這個模子做出來的餅幹,至于這個餅幹是什麼味道的就要看你自己在執行個體化時用的是什麼材料了,你可以做巧克力餅幹,也可以做牛奶餅幹,這些餅幹出了材料不一樣外,其它的東西都是一樣的了。

1.2共享庫版本命名規則

Libname.so.x.y.z,x主版本号,y次版本号,z釋出版本号。

可執行檔案中,儲存了需要共享庫的主版本号。Solaris和linux中的SO-NAME機制記錄了共享庫之間的依賴關系。在linux中,對于共享庫,會存在同目錄下,建立一個指向此共享庫的軟連結。

軟連結的作用是軟連結指向的是最新版本的共享庫(同一主版本下的最新版本),有一個好處就是使得依賴于某個共享庫的子產品,不依賴于詳細的版本号。

SO-NAME表示一個庫的接口。

連結器參數設為 -lc表示按需選擇動态/靜态庫,-static表示靜态,-Bdynamic表示動态庫(預設情況)。

SO-NAME機制加上共享庫的符号版本資訊,保證了主版本号一緻下的程式正常運作。(lunux中并未廣泛運用,Glibc則是)

Linux支援同一函數的多個版本的存在,以便于連結器挑選合适版本的符号進行連結(函數以符号儲存,通過符号定位函數)。

1.3 共享庫查找過程

動态連結的ELF可執行檔案在啟動時會自動啟動動态連結器,程式運作是以來的共享對象全部是由動态連結器負責裝載和初始化。

Linux系統中包含一個ldconfig程式,作用是為共享庫目錄下的各個共享庫建立、删除、更新相應的SO-NAME(符号連結),并會收集起來,集中存放到/etc/ld.so.cache檔案裡邊,并建立一個SO-NAME的緩存,/etc/ld.so.cache的結構被特殊設計,以便于快速查找。

理論上,程式安裝在安裝過程中,都會調用ldconfig程式。不同系統下,上述的兩個檔案名稱或者路徑會不一樣。

1.4環境變量

Linux提供一種方法,即提供環境變量LD_LIBARARY_PATH,改變某個應用程式共享庫的查找路徑,而不影響系統中的其他程式。LD_PRELOAD環境變量裡路徑邊的共享庫和目标檔案都會被優先提前加載。釋出一個版本的程式不應該依賴于LD_PRELOAD。LD_DEBUG環境變量可以打開動态連結器的調試功能,顯示連結中的有用資訊等。

1.5共享庫的建立與安裝

建立共享庫或共享對象,gcc、-shared、-fPIC(position independent code)。

Strip工具可以清除共享庫或者可執行檔案中所有的符号和調試資訊。

或者在gcc時,指定-s(清除所有調試資訊)和-S(清除符号調試資訊)。

共享庫的安裝,**方法一為:将共享庫拷貝到對于目錄下(需要root權限),然後運作ldconfig。方法二,無非也是為了建立相應的SO-NAME軟連結,并告訴編譯器和程式如何查找到該共享庫并正确運作,**此時也需要用到ldconfig,需要制定共享庫所在目錄,即執行如下指令:

1.6共享庫構造函數與析構函數

構造函數即可以幫助共享庫在被裝載時,能夠進行一些初始化工作,如打開檔案、網絡連接配接等。在函數聲明時加上“attribute((constructor))”的屬性,即指定該函數為構造函數,使用“attribute((destructor))”為析構函數用。當用dlopen()打開一個共享庫時,構造函數會在dlopen函數傳回前執行,而析構函數則會在main函數退出,或者程式調用exit函數時執行。如果共享庫時運作時加載的,則會在調用dlclose函數傳回前執行。__attribute__文法是gcc對C、C++的拓展,對其他編譯器文法不通用。多個共享庫的構造函數可以選擇指定優先級按順序執行,或者無順序。

1.7共享庫腳本

上述提到的共享庫都是動态連結的ELF共享對象檔案(.so),事實上共享庫還可以是符合一定格式的連結腳本檔案,即講一個或者多個共享庫按照一個方式組合起來即可,這種腳本檔案的連結過程是動态完成的,也就是運作時完成。LD是一個共享庫,也是一個動态連結腳本。

繼續閱讀