【四、静态库与动态库(共享库)】揭开链接库的神秘面纱:手把手教你制作静态链接库与动态链接库(二)

简介: 【四、静态库与动态库(共享库)】揭开链接库的神秘面纱:手把手教你制作静态链接库与动态链接库

1. 静态库的制作

① 首先把所有 .c 文件编译为 .o 文件

gcc -c *.c -I ../header/

一定要从 .o 文件去生成 .a 文件,否则即使你生成了库,在使用时也可能出现未知的错误。

② 将 .o 文件打包为 .a 文件

这一步需要使用 ar 工具来完成。ar 工具是用来创建, 修改和提取档案的工具,ar 是 archive 的缩写。归档是指将多个文件(或一个文件)放到单个文件中,不进行其他额外的操作。也就是说,归档并不等同于压缩,压缩会使文件的大小减小。

ar rcs libmprint.a *.o
  • 参数 r :在库中插入模块(替换)。当插入的模块名已经在库中存在,则替换同名的模块。如果若干模块中有一个模块在库中不存在,ar 显示一个错误消息,并不替换其他同名模块。默认的情况下,新的成员增加在库的结尾处,可以使用其他任选项来改变增加的位置。
  • 参数 c :创建一个库。不管库是否存在,都将创建。
  • 参数 s :创建目标文件索引,这在创建较大的库时能加快时间。如果不需要创建索引,可改成大写 S 参数。如果 .a 文件缺少索引,可以使用 ranlib 命令添加)。

一般我们命令静态库的时候是这样的,前面是 lib 中间加上我们自己给静态库文件的名称,后面加后缀 .a ,也就是 libxxx.a ,实际上, xxx 才是静态库文件的名称。

可以查看一下生成的 libmprint.a 的内容,一堆乱码

我们可以用 nm 命令查看库文件的信息,可以看到它是由 my_print.o 和 print_hello.o 生成的。

2. 静态库的使用

静态库的使用方法

gcc main.c -o exe -lmyprint -L ../lib/ -I ../header/

首先对上面的命令进行分解一下,-lmyprint 用于指定链接库的名字,我们上面生成了一个名为 libmprint.a 的静态库,把前缀后缀去掉留下中间的名字即可;-L …/lib/ 指定静态库的搜索目录,不指定的会,编译器只会去默认的链接库搜索路径寻找;-I …/header/ 这个就比较熟悉了,指定头文件的路径。

我们再发布的时候,只需要把 headerlib 发布给别人即可

三、自己动手制作动态链接库

1. 动态库的制作

① 编译生成与位置无关的 .o 文件

动态共享库是在运行的时候才加载的,它加载到内存的共享库段,用完后就释放,所以要编译为与位置无关的。这里要用到GCC-fPIC 选项参数,该选项表示编译为位置独立的代码,如果不用这个选项的话编译后的代码是位置相关的,所以动态载入的时候会通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。在静态链接库中,可执行文件在链接时就知道每一行代码、每一个变量会被放到线性地址空间的什么位置,因此这些地址可以都作为常数写到代码里面。对于动态库,只有加载的时候才知道。也就是说,动态链接库要编译为与位置无关的代码,这样只有在运行时才直到代码的位置。而静态库是在生成可执行文件之前,就直接把代码加载到内存的代码段了,代码的位置等信息也就已经知道了,所以不需要生成与位置无关的代码。

gcc -fPIC -c *.c -I ../header/

② 将 .o 文件打包

这里要用到 GCC-shared 选项,该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),如果不用该标志的话外部程序将无法连接。

可以在动态库文件名后面加版本号 libxxx.so.主版本号.副版本号 ,比如 libmdprint.so.1.1 表示我这是 1.1 版本的动态库,文件后缀后面的数字就是版本号的意思。

gcc -shared *.o -o libmdrint.so

2. 动态库的使用

(1)加载动态库

使用动态库的命令和使用静态库的命令一样

gcc main.c -o exe -lmdrint -L ../lib/ -I ../header/

(2)“加载共享库出错”的解决方法

这个是时候,虽然我们已经生成了可执行文件,但是当我们运行可执行文件的时候,可能会出现这样的错误

我们可以通过命令 ldd 来查看一下可执行文件的链接情况

在我们自己引入的库 libmdrint.so 一栏显示 “not found” ,在默认情况下,我们自己引入的动态库是无法使用的,需要进行一些配置。这里提供以下几种解决方法。

① 在系统库路径下建立软链接

前面我们已经介绍了,GCC 会默认去系统库路径下搜索库文件,所以只要我们把自己的库文件放到这个目录下就可以了,系统库的路径是 /usr/lib/lib 。但是我们实际操作的时候,可能无法把文件拷贝到这两个路径下,我们可以在系统库路径下建立一个软链接来指向我们的库文件(软链接相关知识请参考本人 Linux 专栏文章《【Linux王者之路基础篇:基本命令与基础知识】Linux常用shell命令(及相关知识)详解与用法演示》)

sudo ln -s /home/qq/dm/dm_lib/lib/libmdrint.so /usr/lib/libmdrint.so

这里第一个路径也就是我们自己的动态库文件所在目录,一定要用绝对路径(如果使用相对路径,会找不到该路径),第二个路径是系统库的路径。

② 配置环境变量

实际上, ld 链接器在寻找库路径的时候,都是通过一个环境变量 LD_LIBRARY_PATH 来寻找的,我们可以打印看一下这个环境变量

我们可以通过 export 命令来给环境变量增加一个路径,只需要把自己的路径加进去即可,命令如下

export LD_LIBRARY_PATH=/home/qq/dm/dm_lib/lib/:$LD_LIBRARY_PATH

我们知道在 shell 下直接把绝对路径加到环境变量中,只是一种临时的环境变量,当关机再次开机的时候,就会失效,如果想要永久有效,可以修改配置文件。我们可以把上面这条命令放到 .bashrc 文件中。

在我的 Linux 专栏文章中,已经不只一次提到 .bashrc 这个配置文件了,其实 Linux 每次开机都会执行这个配置文件,我们实现环境变量永久有效的原理就是,Linux 每次开机都会执行 .bashrc 配置文件,而配置文件中包含设置环境变量的命令,所以每次开机都会配一下环境变量,这样就达到了环境变量永久有效的目的。 .bashrc 配置文件在家目录下

vim ~/.bashrc

③ 修改 ld.so 加载器的配置文件 ld.so.conf

这里先介绍几个概念:

  • ld链接器 :gcc 可以在编译周期传递参数,指定需要链接的库文件,生命周期是在 compile-time。
  • ld.so动态链接器/加载器,程序运行的时候会根据指定的路径去加载指定的库,生命周期是在 run-time。
  • ld.so加载器及配置文件ld.so.conf:指定动态链接库的搜索路径(也可以通过配置环境变量$LD_LIBRARY_PATH 来指定程序运行时的动态库.so文件的搜索路径)。使用 ldconfig 就可以将 ld.so.conf 中的指定目录的库文件加载到内存中,并记录在/etc/ld.so.cache文件中。配置文件 /etc/ld.so.conf 记录了编译时使用的动态链接库的路径,在默认情况下,编译器只会使用 /lib 和 /usr/lib 这两个目录下的库文件,通过这个配置文件可以增加我们自己的动态库文件搜索路径。

使用 vim 编辑器打开配置文件,把路径放进去

sudo vim /etc/ld.so.conf

放入路径之后,还要在 shell 下执行一条命令,使刚才的配置生效

sudo ldconfig -v

不管使用上面三种方法的哪种,都可以使动态库正常加载到可执行文件中,我们再次使用 ldd 命令查看 exe 文件,可以看到,我们的动态库 libmdrint.so 后面由原来的 “not found” 变成了一个路径,这说明加载器现在可以找到动态库的路径了,可执行文件 exe 也就可以运行了。


总结

学完本文,是不是觉得动态库和静态库也没什么嘛,不过如此,我们大家都可以自己动手去做。那么如果想学习到更多 Linux 下编程的知识,请关注我的 Linux 专栏,查阅其他文章,谢谢大家的支持。

相关文章
|
7月前
|
开发工具 iOS开发
制作传统动态库和静态库遇到的问题
制作传统动态库和静态库遇到的问题
49 1
|
7月前
|
编译器 程序员 C语言
静态库与动态库的构建(含具体代码可以实操)
静态库与动态库的构建(含具体代码可以实操)
60 0
|
存储 编译器 C++
《C++避坑神器·十五》动态库只有dll文件,没有.lib文件时动态调用dll的中类和成员函数
《C++避坑神器·十五》动态库只有dll文件,没有.lib文件时动态调用dll的中类和成员函数
434 0
|
程序员 vr&ar C语言
C/C++静态库和动态库的制作、使用、优缺点
C/C++静态库和动态库的制作、使用、优缺点
234 0
|
Linux 编译器 Shell
【Linux系统编程】静态库和共享库
【Linux系统编程】静态库和共享库
154 0
|
存储 Linux 编译器
【四、静态库与动态库(共享库)】揭开链接库的神秘面纱:手把手教你制作静态链接库与动态链接库(一)
【四、静态库与动态库(共享库)】揭开链接库的神秘面纱:手把手教你制作静态链接库与动态链接库
292 0
【四、静态库与动态库(共享库)】揭开链接库的神秘面纱:手把手教你制作静态链接库与动态链接库(一)
|
NoSQL 编译器 vr&ar
嵌入式(十二)——库文件及静态库与动态库的制作与使用(附练习)
嵌入式(十二)——库文件及静态库与动态库的制作与使用(附练习)
232 0
嵌入式(十二)——库文件及静态库与动态库的制作与使用(附练习)
|
Linux
Linux环境封装静态库成动态库
Linux环境封装静态库成动态库
199 0
|
缓存 Unix Linux
linux系统编程(三)gcc常用技巧与静态库与动态库制作
linux系统编程(三)gcc常用技巧与静态库与动态库制作
280 0
linux系统编程(三)gcc常用技巧与静态库与动态库制作
|
编解码 Java Android开发
so库你应该知道的基础知识
Android系统目前支持以下七种不同的CPU架构:ARMv5,ARMv7 (从2010年起),x86 (从2011年起),MIPS (从2012年起),ARMv8,MIPS64和x86_64 (从2014年起),每一种都关联着一个相应的ABI。
387 0