目錄
文章目錄
- 目錄
- C 語言的庫
- 靜态連結庫
- 生成靜态連結庫
- 動态連結庫
- 生成動态連結庫
- 總結
- 參考文檔
C 語言的庫
C 語言的庫有兩種,靜态連結庫和動态連結庫。在 Linux 中,靜态連結庫命名為 lib*.a,動态庫為 lib*.so。這些庫檔案存在的價值就是抽象出通用的函數,避免重複造輪子。不管是使用哪一種庫,程式員必須在程式中通過 include 來包含相應的頭檔案,并在預編譯階段替換 include 的内容,然後在連結階段将調用到的庫函數從各自所在的檔案庫中連結到合适的地方。

靜态連結庫
靜态連結,即:在連結階段,将源檔案中用到的庫函數與彙編生成的 .o 目标檔案 合并生成 可執行檔案。是以,靜态連結方式的好處是:友善程式移植,因為可執行程式包含了所有庫函數的内容,放在任何環境當中都可以執行。缺點就是:可執行檔案通常會比較大。而且每次庫檔案更新的話,都要重新編譯源檔案,很不友善。
直覺的看,一個全靜态方式生成的簡單 print 程式大小為 857K,而動态連結生成的一樣的可執行檔案隻有 8.4K,因為靜态連結的可執行檔案包含了整理 stdio 庫檔案。
如上圖,對于靜态編譯的程式 1、2,因為都使用了 staticMath 庫。是以在記憶體中就有兩份相同的 staticMath.o 目标檔案,一旦程式數量過多就很可能會記憶體不足,很浪費空間。
生成靜态連結庫
- add.c
#include "add.h"
int add(int a, int b) {
return a + b;
}
- add.h
#ifndef _ADD_H
#define _ADD_H
int add(int a, int b);
#endif
- 生成目标檔案
gcc -c add.c --std c99
- 生成靜态連結庫檔案
ar -crv libadd.a add.o
使用靜态連結庫:
- include
#include <stdio.h>
#include "./add.h"
int main() {
int number1 = 10;
int number2 = 90;
printf("SUM: %d\n", add(number1, number2));
return 0;
}
- 編譯
gcc -std=c99 test.c -o test -L./ -ladd
- -L:指定加載庫檔案的路徑。
- -l:指定加載的庫檔案。
動态連結庫
動态連結,即:在程式運作過程中動态的調用庫檔案。好處是:占空間小、程式檔案小。缺點是:可移植性太差,如果兩台電腦運作環境不同,例如:動态庫存放的位置不一樣、沒有動态庫檔案,就很可能導緻程式運作失敗。
生成動态連結庫
gcc -c add.c --std c99
- 生成動态連結庫檔案
- -shared :指定生成動态連結庫。
- -fPIC :表示編譯為位置獨立的代碼,用于編譯共享庫。目标檔案需要建立成位置無關碼,就是在可執行程式裝載它們的時候,它們可以放在可執行程式的記憶體裡的任何地方。
gcc -fPIC -shared -o libadd.so add.c
- 編譯并動态連結庫檔案
gcc -std=c99 test.c -o test -L./ -ladd
- 執行程式
$ ./test
./test: error while loading shared libraries: libadd.so: cannot open shared object file: No such file or directory
ERR:libadd.so: cannot open shared object file: No such file or directory
因為執行程式找不到 libadd.so,檢視 test 程式的動态連結庫資訊:
$ ldd test
linux-vdso.so.1 => (0x00007fff39fd5000)
libadd.so => not found
libc.so.6 => /lib64/libc.so.6 (0x00007f5410c0b000)
/lib64/ld-linux-x86-64.so.2 (0x00007f5410fd9000)
可以看到 test 執行程式用到的 libadd.so 确實是 not found,這是因為在 /etc/ld.so.conf 檔案中設定了動态連結庫了尋找路徑:
$ cat /etc/ld.so.conf
include ld.so.conf.d/*.conf
$ ll /etc/ld.so.conf.d
總用量 40
-rw-r--r-- 1 root root 26 4月 7 22:41 bind-export-x86_64.conf
-rw-r--r-- 1 root root 24 4月 2 02:32 hcoll.conf
-rw-r--r-- 1 root root 19 4月 2 02:23 ibutils.conf
-r--r--r-- 1 root root 63 4月 1 07:40 kernel-3.10.0-1127.el7.x86_64.conf
-r--r--r--. 1 root root 63 10月 21 2017 kernel-3.10.0-693.5.2.el7.x86_64.conf
-r--r--r-- 1 root root 63 11月 29 2018 kernel-3.10.0-957.1.3.el7.x86_64.conf
-r--r--r-- 1 root root 63 5月 1 22:57 kernel-rt-3.10.0-1127.rt56.1093.el7.x86_64.conf
-rw-r--r-- 1 root root 17 4月 3 01:52 mariadb-x86_64.conf
-rw-r--r-- 1 root root 22 4月 2 02:26 mxm.conf
-rw-r--r-- 1 root root 24 4月 2 02:28 sharp.conf
顯然這裡是沒有 libadd.so 的存儲路徑的,是以我們需要添加一下 libadd.so 的路徑:
$ cat /etc/ld.so.conf.d/test.conf
/root/workspace/test
然後執行 ldconfig 指令生效,再次執行 test 程式:
$ ./test
SUM: 100
總結
綜上說述,靜态和動态連結庫的選擇要視情況而定。通常推薦動态連結方式,因為可以很好的節約記憶體,而且友善後續庫檔案的更新。