1. 库?

库是写好的,现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库。例如最常见的printf()库函数。

2. 静态库与动态库?

2.1. 静态库(.a)

程序在链接的时候把静态库文件链接到我们的程序的目标文件(.o)中,最终形成可执行文件。也就是相当于直接将写好的代码加到了我们的程序中。

静态库的特点:

  • 静态库文件的链接是放在编译时期完成的
  • 程序在运行时与函数库再无瓜葛,移植方便
  • 浪费空间和资源,因为同时运行100个程序可能要把那些常用的静态库链接100遍,费时费力

2.2. 动态库/共享库(.so)

程序在链接的时候,仅仅把动态库中所用到的函数的入口地址做成一张表,而不是把整个库函数链接到程序中。等到程序运行的时候,动态库会被加载到内存中,而程序通过页表的映射就可以访问到这些库函数本体。这也说明了动态库的另一个名字共享库的由来,如果有多个用到同一共享库的程序,操作系统可以只在内存中存储一份共享库,而让这些程序的页表都映射到同一份共享库代码,这样就实现了代码的共享,比较节省空间。

动态库的特点:

  • 动态库把对一些库函数的链接载入推迟到程序运行的时期
  • 可以实现进程之间的资源共享。(因此动态库也称为共享库)
  • 将一些程序升级变得简单

3. 静态库的生成与使用

先写两个功能函数供之后打包成静态库。

// add.c
int add(int a, int b) {
    return a + b;                                                                                                              
}

// sub.c
int sub(int a, int b) {
    return a - b;                                                                                                              
}

现在将这两个函数先分别生成对应的目标文件,再打包成一个静态库。

syndi@ubuntu:~/workspace/sys_code/lib$ gcc -c add.c -o add.o
syndi@ubuntu:~/workspace/sys_code/lib$ gcc -c sub.c -o sub.o
syndi@ubuntu:~/workspace/sys_code/lib$ ls
add.c  add.o  sub.c  sub.o
syndi@ubuntu:~/workspace/sys_code/lib$ ar -rc libmymath.a add.o sub.o
syndi@ubuntu:~/workspace/sys_code/lib$ ls
add.c  add.o  libmymath.a sub.c  sub.o

静态库文件名必须以lib开头,.a结尾。

使用下面的命令可以查看静态库中包含了哪些目标代码:

syndi@ubuntu:~/workspace/sys_code/lib$ ar -tv libmymath.a 
rw-r--r-- 0/0   1248 Jan  1 08:00 1970 add.o
rw-r--r-- 0/0   1240 Jan  1 08:00 1970 sub.o

下面,编写一个调用库函数的测试程序:

// main.c
#include <stdio.h>

extern int add(int, int);
extern int sub(int, int);

int main() {
    int x = add(3, 4); 
    printf("3 + 4 = %d\n", x); 

    int s = sub(3, 1); 
    printf("3 - 1 = %d\n", s);                                                                                                 

    return 0;
}

之后,将测试程序和静态库一起编译:

syndi@ubuntu:~/workspace/sys_code/lib$ gcc main.c -L. -lmymath -o main
syndi@ubuntu:~/workspace/sys_code/lib$ ls
add.c  add.o  libmymath.a  main  main.c  sub.c  sub.o
syndi@ubuntu:~/workspace/sys_code/lib$ ./main
3 + 4 = 7
3 - 1 = 2

可以看到,程序运行良好。

Linux下使用静态库,只需要在用gcc编译的时候用-L选项指定库的搜索路径(上面的例子中使用的是当前路径.),然后用-l选项指定静态库名(不需要lib前缀和.a后缀)即可。

4. 动态库的生成与使用

首先是生成动态库:

syndi@ubuntu:~/workspace/sys_code/lib$ gcc -fPIC -c add.c -o add.o
syndi@ubuntu:~/workspace/sys_code/lib$ gcc -fPIC -c sub.c -o sub.o
syndi@ubuntu:~/workspace/sys_code/lib$ ls
add.c  add.o  sub.c  sub.o
syndi@ubuntu:~/workspace/sys_code/lib$ gcc --share add.o sub.o -o libmymathd.so
syndi@ubuntu:~/workspace/sys_code/lib$ ls
add.c  add.o  libmymathd.so  sub.c  sub.o

选项-fPIC表示生成位置无关代码。--share表示要生成动态库,动态库文件名以lib开头.so结尾。

下来链接动态库:

syndi@ubuntu:~/workspace/sys_code/lib$ gcc -c main.c -o main.o
syndi@ubuntu:~/workspace/sys_code/lib$ gcc main.o -L. -lmymathd -o main
syndi@ubuntu:~/workspace/sys_code/lib$ ls
add.c  add.o  libmymathd.so  main  main.c  main.o  sub.c  sub.o

但此时如果直接运行main程序会报错:

syndi@ubuntu:~/workspace/sys_code/lib$ ./main
./main: error while loading shared libraries: libmymathd.so: cannot open shared object file: No such file or directory

提示找不到共享库,所以此时还要再加一步,告诉操作系统我依赖的共享库在哪:

  • 添加系统变量LD_LIBRARY_PATH指定共享库文件的位置
  • 或者将共享库放到/usr/lib

上面两种方法任选其一

syndi@ubuntu:~/workspace/sys_code/lib$ export LD_LIBRARY_PATH=.
syndi@ubuntu:~/workspace/sys_code/lib$ ls
add.c  add.o  libmymathd.so  main  main.c  main.o  sub.c  sub.o
syndi@ubuntu:~/workspace/sys_code/lib$ ./main
3 + 4 = 7
3 - 1 = 2

此时就可以了。

最后修改:2019 年 11 月 10 日
如果觉得我的文章对你有用,请随意赞赏