动态库(共享库)
最后更新于:2022-04-02 03:52:05
[TOC]
## 动态库
- 静态库的一个缺点是,当同时运行多个应用程序,并且他们都使用来至同一个函数库的函数时候,内存中就会有同一个函数的多个副本,而且程序文件自身也有多个同样的副本,占用磁盘和内存的开销,共享库就是解决重复库包含的问题的
- 共享库保存位置和静态库一致,但后缀名是基于.so区分的
- 当一个程序使用共享库时,它的链接方式是:程序不再包含函数代码,而是应用运行时刻访问的共享代码。
- 当编译好的程序被装载入内存中执行时候,函数引用被解析并产生对共享库的函数调用,如果有必要,共享库才会被加载到内存中(符合按需加载的原则)
- 系统可以只保存一份副本供所有有需要的程序共同使用;另外,共享库函数版本升级时候,仅需要修订一份文件即可
linux系统负责装载共享库并解析执行程序所引用函数的程序是`ld.so`,相关共享库搜索路径配置`/etc/ld.so.conf`,修改该配置后,需要通过`ldconfig`来重载配置,可以基于`ldd`命令查看某个程序所依赖的共享库。
## 创建动态库
#include
void say_hello();
int cal_sum(int x, int y);
#endif
```
生成动态库 方式一: ``` gcc -c -fPIC -o myfunc.o myfunc.c -c 表示只编译 (compile) ,而不链接,输出目标文( obj 文件)。 -o 表示输出文件的文件名。 -fPIC PIC 指 Position Independent Code , 生成适合在共享库中使用的与位置无关的代码。编译成共享库要求此选项。适用于动态链接并避免对全局偏移表大小的任何限制。 gcc -shared myfunc.o -o libmyfunc.so -share 生成一个共享对象,可以与其他对象链接以形成可执行文件。 ``` 方式二:直接分成动态库 ``` gcc -fPIC -shared myfunc.c -o libmyfunc.so ``` **其实动态链接库有其命名规则**。 ``` 如 libname.so.x.y.z lib :统一前缀; name :动态链接库的名字, libmyfunc.so 中 myfunc 就是其动态链接库名; so :统一后缀; x :主版本号,表示该库有重大更新,不同版本号之间是不兼容的; y :次版本号,表示该库增量升级,在相同主版本号下,次版本号高的兼容次版本号低的库; z :发布版本号,表示该库的优化、 bugfix 等,相同主次版本号,不同发布版本号之间完全兼容 ``` ## 使用动态链接库
生成执行文件 ``` gcc -o test test.c -l myfunc -L . // 或 gcc -o test test.c -lmyfunc -L . -l 选项说明库文件的名字 -L 选项说明库文件的路径。`-L .` ,表示当前路径 ``` 生成的 可执行文件依然会报错 因为:**执行程序的时候,系统不知道`libmyfunc.so`的位置,系统无法找到库文件**。尽管我们用 GCC 编译的时候,通过`-L`选项提供了`libmyfunc.so`文件的位置。**但是这个信息没有被写入到可执行程序里面** 可通过 ldd 查看 ``` ldd test linux-vdso.so.1 => (0x00007fff57fb0000) libmyfunc.so => not found libc.so.6 => /lib64/libc.so.6 (0x00007f1fde4fc000) /lib64/ld-linux-x86-64.so.2 (0x00007f1fde8ca000) ``` ## 找不到 .so 的4种方式 ### 放到 GCC 默认搜索目录 **将`libmyfunc.so`放到 GCC 默认搜索目录。** 比如`/usr/lib/x86_64-linux-gnu`或者`/lib/x86_64-linux-gnu`,或者 `/usr/lib64`都可以,这样做简单粗暴。但是这样做需要 root 权限来完成。 ### 使用 /etc/ld.so.conf.d **在`/etc/ld.so.conf.d`目录下新建一个`.conf`文件。** 比如`mylib.conf`,在里面添加第三方库目录的绝对路径(比如`libmyfunc.so`所在目录的绝对路径) ### 设置 LD_LIBRARY_PATH 环境变量 比如`export LD_LIBRARY_PATH=.`(`.`代表当前目录)。当设置这个环境变量后,操作系统将在`LD_LIBRARY_PATH`下搜索库文件,再到默认路径中搜索文件 如果需要永久添加变量,需要将`export LD_LIBRARY_PATH=/xxx/xxx:$LD_LIBRARY_PATH`写入到`~/.bashrc`里面,其中`/xxx/xxx`是库文件所在目录的绝对路径 ### 编译的时候添加 -Wl,-rpath 选项 ``` gcc -o test test.c -l myfunc -L . -Wl,-rpath=. -Wl 选项告诉编译器将后面的参数传递给链接器 ``` 添加此参数后, 可执行文件便会在当前路径下查看 lib库
';
myfunc.c
``` #include "myfunc.h" void say_hello() { printf("hello world\n"); } int cal_sum(int x, int y) { return x + y; } ```myfunc.h
``` #ifndef __MYFUNC_H #define __MYFUNC_H #include生成动态库 方式一: ``` gcc -c -fPIC -o myfunc.o myfunc.c -c 表示只编译 (compile) ,而不链接,输出目标文( obj 文件)。 -o 表示输出文件的文件名。 -fPIC PIC 指 Position Independent Code , 生成适合在共享库中使用的与位置无关的代码。编译成共享库要求此选项。适用于动态链接并避免对全局偏移表大小的任何限制。 gcc -shared myfunc.o -o libmyfunc.so -share 生成一个共享对象,可以与其他对象链接以形成可执行文件。 ``` 方式二:直接分成动态库 ``` gcc -fPIC -shared myfunc.c -o libmyfunc.so ``` **其实动态链接库有其命名规则**。 ``` 如 libname.so.x.y.z lib :统一前缀; name :动态链接库的名字, libmyfunc.so 中 myfunc 就是其动态链接库名; so :统一后缀; x :主版本号,表示该库有重大更新,不同版本号之间是不兼容的; y :次版本号,表示该库增量升级,在相同主版本号下,次版本号高的兼容次版本号低的库; z :发布版本号,表示该库的优化、 bugfix 等,相同主次版本号,不同发布版本号之间完全兼容 ``` ## 使用动态链接库
test.c
``` #include "myfunc.h" int main(int argc, char const *argv[]) { int result = 0; say_hello(); result = cal_sum(2, 3); printf("%d\n", result); return 0; } ```生成执行文件 ``` gcc -o test test.c -l myfunc -L . // 或 gcc -o test test.c -lmyfunc -L . -l 选项说明库文件的名字 -L 选项说明库文件的路径。`-L .` ,表示当前路径 ``` 生成的 可执行文件依然会报错 因为:**执行程序的时候,系统不知道`libmyfunc.so`的位置,系统无法找到库文件**。尽管我们用 GCC 编译的时候,通过`-L`选项提供了`libmyfunc.so`文件的位置。**但是这个信息没有被写入到可执行程序里面** 可通过 ldd 查看 ``` ldd test linux-vdso.so.1 => (0x00007fff57fb0000) libmyfunc.so => not found libc.so.6 => /lib64/libc.so.6 (0x00007f1fde4fc000) /lib64/ld-linux-x86-64.so.2 (0x00007f1fde8ca000) ``` ## 找不到 .so 的4种方式 ### 放到 GCC 默认搜索目录 **将`libmyfunc.so`放到 GCC 默认搜索目录。** 比如`/usr/lib/x86_64-linux-gnu`或者`/lib/x86_64-linux-gnu`,或者 `/usr/lib64`都可以,这样做简单粗暴。但是这样做需要 root 权限来完成。 ### 使用 /etc/ld.so.conf.d **在`/etc/ld.so.conf.d`目录下新建一个`.conf`文件。** 比如`mylib.conf`,在里面添加第三方库目录的绝对路径(比如`libmyfunc.so`所在目录的绝对路径) ### 设置 LD_LIBRARY_PATH 环境变量 比如`export LD_LIBRARY_PATH=.`(`.`代表当前目录)。当设置这个环境变量后,操作系统将在`LD_LIBRARY_PATH`下搜索库文件,再到默认路径中搜索文件 如果需要永久添加变量,需要将`export LD_LIBRARY_PATH=/xxx/xxx:$LD_LIBRARY_PATH`写入到`~/.bashrc`里面,其中`/xxx/xxx`是库文件所在目录的绝对路径 ### 编译的时候添加 -Wl,-rpath 选项 ``` gcc -o test test.c -l myfunc -L . -Wl,-rpath=. -Wl 选项告诉编译器将后面的参数传递给链接器 ``` 添加此参数后, 可执行文件便会在当前路径下查看 lib库