1. 引言
静态链接库和动态库均为函数库

函数库:不是C语言的一部分,是一些事先写好的函数的集合,给别人复用
就像scanf和printf函数一样,通过#include <stdio.h>,即可调用
早期并没有函数库,只是后来的程序员们通过整理把日常用的函数进行合并,形成一份完整的函数库,就是现在的标准函数库,例如:glibc

 

静态链接库:

函数库源代码经过只编译不链接形成的.o目标文件,然后通过ar工具将.o文件归档成.a静态链接库文件
商业公司通过发布.h头文件和.a静态链接库文件给用户使用
用户拿到.a和.h文件,通过.h文件得知函数库内的函数原型,然后在自己的.c文件中直接调用这些库函数
在链接形成可执行程序过程中:链接器会在.a文件中找到对应的.o文件
缺点:如果多个应用程序都使用了同一个静态库的库函数时,则会导致每个应用程序在生成可执行程序中,都各自复制了一份库函数的代码,这些应用程序如果同时运行,在系统内存中则会存在多个库函数的副本,很浪费内存
动态链接库(.so Shared Object共享库)

优点:不像静态链接库那样,拷贝库函数的代码到可执行程序中,而是在可执行程序需要调用到库函数的位置做了标记,当可执行程序运行到调用该库函数的位置,会自动将该动态库加载到内存,以后不管多少个应用程序同时运行,该库函数在内存中只有一份

 

2. 制作静态链接库
 

mylib.c

#include <stdio.h>
int Max(int a, int b)
{
        return (a > b) ? a : b;
}

void PrintMaxNumber(int a, int b)
{
        printf("The max is %d.\n", Max(a, b));
}

 

mylib.h

int Max(int a, int b);

void PrintMaxNumber(int a, int b);

 

将 mylib.c 编译为 mylib.o 目标文件
gcc -c mylib.c -o mylib.o

 

使用 ar 工具,将 mylib.o 目标文件打包为 libmylib.a 静态链接库文件
ar -rc libmylib.a mylib.o

 

3. 使用静态链接库
 

test.c

#include <stdio.h>
#include "mylib.h"

int main()
{
        int a = 5;
        int b = 3;
        int max = Max(a, b);

        printf("The max number is %d.\n", max);

        PrintMaxNumber(a, b);
        return 0;
}

 

将 test.c 编译链接为可执行程序
gcc test.c -o test -lmylib -L.

运行 test 可执行程序,查看结果
mrs@mrs-virtual-machine:~/Desktop$ ./test
The max number is 5.
The max is 5.

 

4. 制作动态链接库
 
编译链接生成 mylib.o
gcc -c mylib.c -o mylib.o -fPIC
 

将 mylib.o 打包生成 libmylib.so 动态链接库文件
gcc -o libmylib.so mylib.o -shared

 

将 /Desktop 目录(因为我的libmylib.so就是在这个目录)加入到 LD_LIBRARY_PATH 动态链接库路径下
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/Desktop

 

5. 使用动态链接库
 
将 test.c 编译链接为 test 可执行程序
gcc test.c -o test -lmylib -L.

 

运行 test 可执行程序,得到运行结果
mrs@mrs-virtual-machine:~/Desktop$ ./test
The max number is 5.
The max is 5.

 

附录
功能	        命令
nm libmylib.a	查看当前 .a 文件内都有哪些符号(函数名)
-lxxx	        该命令为GCC编译链接的参数,指定链接时用到哪些函数库,xxx表示函数库的名称
ldd test	查看可执行程序 test 使用到哪些共享库,以及这些共享库是否能被加载解析

函数寻址

非动态链接:
* 1、 编译阶段: 函数地址、全局变量先设置为0, 这个时候好无法确定地址

* 2、 链接阶段: 链接器根据目标文件或静态库中的"重定位表"(.reloc),找到需要重定位的函数、全局变量,进行重定位,修正他们的地址

 
 

动态链接:

  • 程序运行后会生成一张"全局偏移表"(got), got中记录了需要动态调用的函数偏移地址(函数在动态库中),从而通过got,找到函数。
  • 延迟加载:并不是一开始就找到动态函数地址,因为太耗时间。 所以got一开始指向的是"桩代码"(stub),
    第1次调用的时候,stub会去找到函数地址并记录下来,之后就可以直接用了

 

总结

1. 静态库相当于直接把代码插入到生成的可执行文件中,会导致体积变大,但是只需要一个文件即可运行

2. 动态库则只在生成的可执行文件中生成“插桩”函数,当可执行文件被加载时会读取指定目录中的.dll文件,加载到内存中空闲的位置,并且替换相应的“插桩”指向的地址为加载后的地址,这个过程称为重定向。这样以后函数被调用就会跳转到动态加载的地址去。

Windows:可执行文件同目录,其次是环境变量%PATH%
Linux:ELF格式可执行文件的RPATH,其次是/usr/lib等

posted on 2022-09-08 10:46  残月影歌  阅读(1801)  评论(0编辑  收藏  举报