动态库 3个月前

编程语言
511
动态库

动态链接库是程序运行时加载的库,当动态链接库正确部署之后,运行的多个程序可以使用同一个加载到内存中的动态库,因此在Linux中动态链接库也可称之为共享库。 库中函数和变量的地址使用的是相对地址(静态库中使用的是绝对地址),其真实地址是在应用程序加载动态库时形成的。

  • 在Linux中动态库以lib作为前缀, 以.so作为后缀, 即: libxxx.so
  • 在Windows中动态库一般以lib作为前缀, 以dll作为后缀, 即: libxxx.dll

1.生成动态链接库

生成动态链接库是直接使用gcc命令并且需要添加-fPIC(-fpic) 以及-shared 参数。

  • -fPIC 或 -fpic 参数的作用是使得 gcc 生成的代码是与位置无关的,也就是使用相对位置。
  • -shared参数的作用是告诉编译器生成一个动态链接库。

image

具体步骤如下:

#将源文件进行汇编,并添加 -fpic参数
gcc 源文件(*.c) -c -fpic

#打包.o文件成动态库,使用gcc的 -shared参数
gcc -shared 与位置无关的目标文件(*.o) -o 动态库(libxxx.so)

#发布动态库和头文件

eg:

#汇编
gcc add.c div.c mult.c sub.c -c -fpic -I ./include/

#打包
gcc -shared add.o div.o mult.o sub.o -o libcalc.so  

#发布

2.动态库的使用

当我们同静态库一样编译测试程序为可执行程序后

gcc main.c -o app -L ./ -l calc

执行

./app 
#./app: error while loading shared libraries: libcalc.so: 
#cannot open shared object file: No such file or directory

会有以上错误:无法加载到动态库


3.解决动态库无法加载的问题

①库的工作原理

  • 静态库如何被加载? 在程序编译的最后一个阶段也就是链接阶段,提供的静态库会被打包到可执行程序中。当可执行程序被执行,静态库中的代码也会一并被加载到内存中,因此不会出现静态库找不到无法被加载的问题。
  • 动态库如何被加载?

  • 在程序编译的最后一个阶段也就是链接阶段: 在gcc命令中虽然指定了库路径(使用参数 -L ), 但是这个路径并没有记录到可执行程序中,只是检查了这个路径下的库文件是否存在。 同样对应的动态库文件也没有被打包到可执行程序中,只是在可执行程序中记录了库的名字。

  • 可执行程序被执行起来之后: 程序执行的时候会先检测需要的动态库是否可以被加载,加载不到就会提示上边的错误信息 当动态库中的函数在程序中被调用了, 这个时候动态库才加载到内存,如果不被调用就不加载 动态库的检测和内存加载操作都是由动态连接器来完成的

②动态链接器

动态链接器是一个独立于应用程序的进程, 属于操作系统, 当用户的程序需要加载动态库的时候动态连接器就开始工作了,显然动态连接器不知道用户通过 gcc 编译程序的时候通过参数 -L指定的路径。 那么动态链接器是如何搜索某一个动态库的呢,在它内部有一个默认的搜索顺序,按照优先级从高到低的顺序分别是:

  1. 可执行文件内部的 DT_RPATH 段
  2. 系统的环境变量 LD_LIBRARY_PATH
  3. 系统动态库的缓存文件 /etc/ld.so.cache
  4. 存储动态库/静态库的系统目录 /lib/, /usr/lib

找到后结束遍历,找不到就报错

③解决方法

只需要将动态库的路径放到对应的环境变量或者系统配置文件中,同样也可以将动态库拷贝到系统库目录(或者是将动态库的软链接文件放到这些系统库目录中)。

方法1:将库路径添加到环境变量 LD_LIBRARY_PATH 中

  1. 找到相关的配置文件 用户级别: ~/.bashrc 系统级别: /etc/profile

  2. 使用vim打开配置文件,文件最后添加

    export LIBRARY_PATH=$LIBRARY_PATH:动态库的绝对路径
    
  3. 让修改的配置文件生效

  4. 修改了用户级别的配置文件,关闭当前终端,打开一个新的就生效

  5. 修改了系统级别的配置文件,注销关闭系统,再开机,配置就生效了

  6. 或者执行命令

# 修改的是哪一个就执行对应的那个命令
# source 可以简写为一个 . , 作用是让文件内容被重新加载
source ~/.bashrc          (. ~/.bashrc)
source /etc/profile       (. /etc/profile)

方法2:更新 /etc/ld.so.cache 文件

  1. 找到动态库所在的绝对路径(不包括库的名字)比如:/home/robin/Library/
  2. 使用vim 修改 /etc/ld.so.conf 文件, 将上边的路径添加到文件中(独自占一行)
#1.打开文件
sudo vim /etc/ld.so.conf

#2.添加动态库路径,并且保存退出
  1. 更新 /etc/ld.so.conf中的数据到 /etc/ld.so.cache
sudo ldconfig

方法3:拷贝动态库文件到系统库目录 /lib/ 或者 /usr/lib 中 (库的软链接文件放进去)

# 库拷贝
sudo cp /xxx/xxx/libxxx.so /usr/lib

# 创建软连接
sudo ln -s /xxx/xxx/libxxx.so /usr/lib/libxxx.so

但并不建议直接拷贝,否则再更新时会很麻烦,还要重新拷贝 二者中优解是软链接


④验证

#语法格式
ldd 可执行程序名

通过这种方法可以知道程序是否可以通过动态链接器加载到对应的动态库 如果其后都有地址就说明可以


动态库和静态库的优缺点


静态库

  • 优点

  • 静态库被打包到应用程序中加载速度快

  • 发布程序无需提供静态库,移植方便

  • 缺点

  • 相同的库文件数据可能在内存中被加载多份, 消耗系统资源,浪费内存

  • 库文件更新需要重新编译项目文件, 生成新的可执行程序, 浪费时间。

image


动态库

  • 优点

  • 可实现不同进程间的资源共享

  • 动态库升级简单, 只需要替换库文件, 无需重新编译应用程序

  • 程序可以控制何时加载动态库, 不调用库函数动态库不会被加载

  • 缺点

  • 载速度比静态库慢, 但以现在计算机的性能可以忽略

  • 发布程序需要提供依赖的动态库

image

image
EchoEcho官方
无论前方如何,请不要后悔与我相遇。
1377
发布数
439
关注者
2222615
累计阅读

热门教程文档

Djiango
17小节
Spring Boot
24小节
Typescript
31小节
Flutter
105小节
C#
57小节