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/ 这个就比较熟悉了,指定头文件的路径。
我们再发布的时候,只需要把 header 和 lib 发布给别人即可
三、自己动手制作动态链接库
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 专栏,查阅其他文章,谢谢大家的支持。