庫文件是程式碼複用的核心載體。Linux 下靜態庫與動態庫在建立流程、鏈接方式、執行時行為、部署策略上有本質差異。本文以實戰案例完整演示兩類庫的全生命週期管理。
libtest/
├── lt.c // 庫實作
├── lt.h // 介面宣告
└── test.c // 主程式
lt.h
void myprint(void);
lt.c
#include <stdio.h>
void myprint(void) {
printf("Linux library test!\n");
}
test.c
#include "lt.h"
int main(void) {
myprint();
return 0;
}
gcc -c lt.c -o lt.o # 編譯目標檔
ar rcs liblt.a lt.o # 打包靜態庫(.a)
gcc test.c -L. -llt -o test_static
# 或直接指定庫檔:gcc test.c liblt.a -o test_static
ldd test_static
# 輸出僅顯示系統庫(libc、ld-linux),無 liblt.a 蹤影
原因:靜態庫代碼在鏈接階段已完整複製進執行檔,執行時無外部依賴。
gcc -c lt.c -o lt.o
gcc -shared -fPIC -o liblt.so lt.o
# -shared:產生動態共享庫
# -fPIC:位置無關代碼,支援多進程共享代碼段(核心優勢)
| 方式 | 命令 | 關鍵點 |
|---|---|---|
| 系統目錄法 | sudo cp liblt.so /usr/lib |
鏈接器自動搜尋 /usr/lib;執行時動態連結器自動載入 |
| 指定路徑法 | gcc test.c -L. -llt -o test_dyn2 |
鏈接時用 -L 指定目錄;執行時必須設定 LD_LIBRARY_PATH 否則報錯 |
| rpath 嵌入法 | gcc test.c -L. -llt -Wl,-rpath,. -o test_dyn3 |
將庫搜尋路徑嵌入執行檔,部署最省心(推薦生產環境) |
ldd test_dyn1
# 輸出包含:liblt.so => /usr/lib/liblt.so (0x...)
關鍵差異:動態庫不複製代碼,僅保留符號引用,執行時由動態連結器載入,實現代碼段共享、降低耦合、便於升級。
| 維度 | 靜態庫 | 動態庫 |
|---|---|---|
| 副檔名 | .a | .so |
| 代碼複製時機 | 鏈接階段(靜態鏈接) | 執行階段(動態載入) |
| 執行檔體積 | 較大(含庫代碼) | 較小(僅符號表) |
| 記憶體佔用 | 每進程獨佔庫代碼副本 | 多進程共享單一代碼段 |
| 庫升級影響 | 需重新鏈接所有執行檔 | 替換 .so 即時生效(API 相容前提) |
| 部署依賴 | 無(獨立執行檔) | 需確保 .so 可被動態連結器找到 |
| 符號解析速度 | 啟動快(無動態鏈接開銷) | 啟動稍慢(需符號重定位) |
| 適用場景 | 嵌入式、靜態分發、極致效能 | 通用應用、插件架構、系統庫、頻繁更新 |
liblt.so.1.0.0 → liblt.so.1 → liblt.so 三層軟連結,支援主/次版本相容。-fvisibility=hidden + __attribute__((visibility("default"))) 隱藏內部符號,減少衝突。LD_DEBUG=libs ./test_dyn 觀察動態載入全過程。現代 Linux 開發預設動態鏈接(GCC 預設),兼顧記憶體效率與維護彈性。靜態鏈接保留於特定場景。掌握 -fPIC、rpath、版本軟連結、符號可見性等進階技巧,是構建健壯、可演進系統庫的關鍵能力。