前言
靜態鏈接庫會編譯進可執行文件,並被加載到內存,會造成空間浪費
靜態鏈接庫對程序的更新、部署、發布帶來麻煩。如果靜態庫更新了,使用它的應用程序都需要重新編譯、發布給用戶(對於玩家來說,可能是一個很小的改動,卻導致整個程序重新下載,全量更新)
動態庫在程序編譯時並不會被鏈接到目標代碼中,而是在執行文件中記錄對動態庫的引用,在程序運行時才被載入。不同的應用程序如果調用相同的庫,那么在內存里只需要有一份該共享庫的實例,規避了空間浪費問題。動態庫在程序運行時才被載入,也解決了靜態庫對程序的更新、部署和發布帶來的麻煩,用戶只需要更新動態庫即可,增量更新。
Linux下動態庫文件的文件名形如 libxxx.so,其中so是 Shared Object 的縮寫,即可以共享的目標文件。
特點
- 動態庫把對一些庫函數的鏈接載入推遲到程序運行的時期
- 可以實現進程之間的資源共享。(因此動態庫也稱為共享庫)
- 將一些程序升級變得簡單。
- 甚至可以真正做到鏈接載入完全由程序員在程序代碼中控制(顯示調用)
生成動態鏈接庫
- 編寫源文件
- 將一個或幾個源文件編譯鏈接,生成共享庫。
- 通過 -L<path> -lxxx 的gcc選項鏈接生成的libxxx.so。
- 把 libxxx.so 放入鏈接庫的標准路徑,或指定 LD_LIBRARY_PATH,才能運行鏈接了libxxx.so的程序。
例子
源文件
add.c
int add(int a, int b) { return a + b; }
subtract.c
int subtract(int a, int b) { return a - b; }
編譯
gcc -fPIC -c add.c gcc -fPIC -c subtract.c
其中,PIC是 Position Independent Code 的縮寫,表示要生成位置無關的代碼,這是動態庫需要的特性
鏈接
gcc -shared -o libmymath.so subtract.o add.o
-shared是鏈接選項,告訴gcc生成動態庫而不是可執行文件
這兩步可以合並成一個命令:
gcc -o libmymath.so -fPIC -shared subtract.c add.c
編寫頭文件
#ifndef _MYMATH_H_ #define _MYMATH_H_
int add(int, int); int subtract(int, int); #endif
使用鏈接庫
main.c
#include <stdio.h> #include "mymath.h"
int main() { printf("%d, %d\n", add(2, 3), subtract(9, 8)); return 0; }
鏈接
gcc main.c -L. -lmymath
如果同一目錄下同時存在同名的動態庫和靜態庫,比如 libmymath.so 和 libmymath.a 都在當前路徑下,則gcc會優先鏈接動態庫。
運行
當執行 ./a.out,發現程序報錯
./a.out: error while loading shared libraries: libmymath.so: cannot open shared object file: No such file or directory
這涉及到程序如何定位共享庫文件
- 當系統加載可執行代碼時候,能夠知道其所依賴的庫的名字,但是還需要知道絕對路徑。此時就需要系統動態載入器(dynamic linker/loader)
- 對於elf格式的可執行程序,是由ld-linux.so*來完成的,它先后搜索elf文件的 DT_RPATH段 -- 環境變量LD_LIBRARY_PATH -- /etc/ld.so.cache文件列表 -- /lib/,/usr/lib 目錄找到庫文件后將其載入內存。
/etc/ld.so.cache 是 ldconfig 程序讀取 /etc/ld.so.conf 文件生成的
所以,我們想讓程序找到自己寫的動態鏈接庫,有如下方法:
- 指定 LD_LIBRARY_PATH 環境變量為 libmymath.so 文件所在的目錄
- 編輯/etc/ld.so.conf文件,加入庫文件所在目錄的路徑; 同時,運行ldconfig ,該命令會重建/etc/ld.so.cache文件
- 把 libmymath.so 拷貝到/lib或者/usr/lib下,那么ld默認能夠找到,無需其他操作
我們可以執行:
LD_LIBRARY_PATH=. ./a.out
Makefile
.PHONY: all all: build target build: libmymath.so libmymath.so: add.o subtract.o gcc -o $@ -shared $^ add.o: add.c gcc -c -fPIC $< subtract.o: subtract.c gcc -c -fPIC $< target: a.out a.out: main.c gcc main.c -L. -lmymath .PHONY: clean clean: rm *.o a.out libmymath.so
