盘古开天辟地。咱们写了个程序,想要给终端输出一些内容,不可避免地咱们须要使用系统库,在咱们写程序的过程当中咱们常常会碰到须要使用库的过程,不管是系统库仍是第三方库,咱们统称为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