盤古開天闢地。咱們寫了個程序,想要給終端輸出一些內容,不可避免地咱們須要使用系統庫,在咱們寫程序的過程當中咱們常常會碰到須要使用庫的過程,不管是系統庫仍是第三方庫,咱們統稱爲lib
庫。linux
而庫的連接分爲兩種,分別有靜態庫和動態庫。bash
靜態庫能夠看做一堆的目標文件的集合,可能包含了不少函數的實現。在linux
最經常使用的C語言靜態庫libc
位於/usr/lib/libc.a
,它由成百上千個C語言程序,好比輸入輸出有printf.o,scanf.o
,文件操做有fread.o
,fwrite.o
等等。把這些零散的文件提供給使用者會形成不便,因而一般人們用ar
壓縮程序將這些目標文件壓縮到一塊兒,而後對這些文件進行編號和索引,最後造成了libc.a
這個文件。函數
運行命令查看其中包含的內容,而後過濾查看fread
code
$ ar -t /usr/lib/x86_64-linux-gnu/libc.a | grep fread iofread.o __freading.o __freadable.o iofread_u.o fread_chk.o fread_u_chk.o
若是咱們要在其中尋找printf.o
文件可使用命令索引
$ objdump -t /usr/lib/x86_64-linux-gnu/libc.a | grep printf.o vfprintf.o: 文件格式 elf64-x86-64 vprintf.o: 文件格式 elf64-x86-64 reg-printf.o: 文件格式 elf64-x86-64 fprintf.o: 文件格式 elf64-x86-64 printf.o: 文件格式 elf64-x86-64 snprintf.o: 文件格式 elf64-x86-64 sprintf.o: 文件格式 elf64-x86-64 asprintf.o: 文件格式 elf64-x86-64 dprintf.o: 文件格式 elf64-x86-64 vfwprintf.o: 文件格式 elf64-x86-64 fxprintf.o: 文件格式 elf64-x86-64 iovsprintf.o: 文件格式 elf64-x86-64 fwprintf.o: 文件格式 elf64-x86-64 swprintf.o: 文件格式 elf64-x86-64 vwprintf.o: 文件格式 elf64-x86-64 wprintf.o: 文件格式 elf64-x86-64 vswprintf.o: 文件格式 elf64-x86-64 vasprintf.o: 文件格式 elf64-x86-64 iovdprintf.o: 文件格式 elf64-x86-64 vsnprintf.o: 文件格式 elf64-x86-64 obprintf.o: 文件格式 elf64-x86-64
能夠看到printf.o
文件就在其中。it
假如以咱們寫的helloworld.c
程序爲例io
#include <stdio.h> int main(int argc, char *argv[]) { printf("Hello,World!\n"); return 0; }
默認狀況下直接運行class
$ gcc helloworld.c -o helloworld
會進行動態連接,咱們查看生成的文件大小變量
$ ls -l helloworld -rwxr-xr-x 1 gnc gnc 8304 10月 11 15:06 helloworld
能夠看到大小爲8303bytes
,咱們使用選項-static
來使gcc
進行靜態連接:gcc
$ gcc -static helloworld.c -o helloworld
查看大小
$ ls -l helloworld -rwxr-xr-x 1 gnc gnc 844704 10月 11 15:09 helloworld
能夠看到大小顯著變大。
靜態連接好處就是連接完成之後,運行程序不須要原來的支持庫,但缺點是文件大小較大,而且更新困難。
因此出現了動態連接,也就是不要靜態地進行連接,而是等到動態運行時再進行連接。動態連接的符號定位是發生在運行時首先進行地址空間分配,而後再來運行程序。
具體的連接過程也比較複雜,咱們只須要知道如何來建立這種動態庫便可。
源文件add.c
/* add.h */ void setSummand(int summand); int add(int summand); /* add.c */ #include <stdio.h> int gSummand; void setSummand(int summand) { gSummand = summand; } int add(int summand) { return gSummand + summand; } void __attribute__ ((constructor)) initLibrary(void) { // // Function that is called when the library is loaded // printf("Library is initialized\n"); gSummand = 0; } void __attribute__ ((destructor)) cleanUpLibrary(void) { // // Function that is called when the library is »closed«. // printf("Library is exited\n"); }
源文件answer.c
/* answer.h */ int answer(); /* answer.c */ #include "add.h" int answer() { setSummand(20); return add(22); // Will return 42 (=20+22) }
源文件main.c
#include <stdio.h> #include "add.h" #include "answer.h" int main(int argc, char* argv[]) { setSummand(5); printf("5 + 7 = %d\n", add(7)); printf("And the answer is: %d\n", answer()); return 0; }
而後咱們建立目錄.bin/static
和./bin/shared
。
完成之後的目錄結構以下
$ tree . ├── add.c ├── add.h ├── answer.c ├── answer.h ├── bin │ ├── shared │ └── static └── main.c 3 directories, 5 files
咱們運行命令生成目標文件
$ gcc -c main.c -o bin/main.o # 靜態庫 $ gcc -c add.c -o bin/static/add.o $ gcc -c answer.c -o bin/static/answer.o # 動態庫 gcc -c -fPIC add.c -o bin/shared/add.o gcc -c -fPIC answer.c -o bin/shared/answer.o
運行命令
$ ar rcs bin/static/libtq84.a bin/static/add.o bin/static/answer.o
將兩個目標文件打包壓縮爲一個靜態庫文件。
靜態連接
$ gcc bin/main.o -Lbin/static -ltq84 -o bin/static-main
運行
$ ./bin/static-main Library is initialized 5 + 7 = 12 And the answer is: 42 Library is exited
運行命令
$ gcc -shared bin/shared/add.o bin/shared/answer.o -o bin/shared/libtq84.so
而後進行動態連接
$ gcc bin/main.o -Lbin/shared -ltq84 -o bin/use-shared-library
運行
$ ./bin/use-shared-library ./bin/use-shared-library: error while loading shared libraries: libtq84.so: cannot open shared object file: No such file or directory
會提示找不到動態連接庫,咱們須要修改LD_LIBRARY_PATH
變量來指向咱們的動態庫路徑
export LD_LIBRARY_PATH=$(pwd)/bin/shared
而後再運行
./bin/use-shared-library Library is initialized 5 + 7 = 12 And the answer is: 42 Library is exited
就能夠了,固然你也能夠選擇將該動態庫放置到路徑/usr/lib
下,運行下面的命令
sudo mv bin/shared/libtq84.so /usr/lib sudo chmod 755 /usr/lib/libtq84.so