Linux的静态库和共享库详解及实例
库的本质上是一个可执行的二进制文件,但是它并不能独立的执行。简单的来说,就相当于一个仓库,把你已经写好的功能函数放到库中,然后后续需要时通过正确的接口去使用相应的功能,当然可以把库分享给别人也很方便。在Linux下分为静态库和共享库(也叫动态库),当然Windows下也有静态库(.lib)和动态库(.dll),这里主要是讲解Linux下的静态库和共享库,以及它们的简单实现。
在Linux中静态库以.a为后缀,首先静态库的实现是在文件编译后的链接过程中就被载入到了程序中,那么后续的程序的运行使用就不会再依赖库,那么弊端就是将静态库载入到程序中会导致程序的体积变大,而且如果静态库后续进行了更新,那么程序就需要重新编译会有些麻烦。
而共享库以.so为后缀,它的实现方式是在链接的过程中不会将库载入到程序中,而是生成一个记录表,当程序运行的时候再去通过记录表去链接共享库,那么这个的好处就是程序的体积小,而且当有不同的程序公用一个库的时候,就只需要链接这一个库就够了,并且共享库后续的更新依据其版本号也比较方便,但是运行程序时需要依赖共享库。
对于两种库的使用,需要结合实际情况来选择使用。下面用加减乘除的例子来简单的实现一下两个库的创建。我所用的系统是Ubuntu18.04 server版,首先我先把文件夹和一些文件都提前创建好了。
首先我们先将这些函数文件编译为.o文件,需要加上-fPIC参数,表示让生成的.o文件与路径无关,这一点主要是用于共享库的操作,单纯只用静态库的话可以不加。
静态库
对于静态库的创建用ar命令:ar rcs 静态库名.a *.o,然后我们就得到了一个.a的静态库,然后我将其放到了lib文件夹下。
然后我们将其链接成可执行文件观察一下效果,因为所有的参数都是4 2,所以最终的结果就是6 2 8 2。
共享库
对于一个共享库来说,它可以有三个名字,虽然这不是必须的,但是对于有后续的版本的维护或者更新来说,最好是规范的使用这三个名字,可以极大的方便我们去维护和更新共享库。共享库包括realname soname linkname三个名字,realname的结构如下
libXXXX.so是一般不变的,后面的版本号可能会随着维护和更新从而做出改变,一般来说发行版本号的增加是修改了一些bug或者做了一些优化,调用时不会有其他的影响。次版本号是向下兼容的,也就是说libxxx.so.1.2.0是对libxxx.so.1.1.0兼容,也就是说在原有的接口不变的情况下添加了一些新的特性,提供了新的功能。主版本号的增加就说明了它是不兼容的,需要重新编译链接。通常我们都会将soname和linkname分别与realname建立软连接,那么链接soname和linkname实际上就是链接到了realname。那么libXXXX.so.X就是soname,libXXXX.so是linkname。
下面就还是以上面的例子来创建一个共享库。首先我们要对.cpp文件进行编译,那么-fPIC这个命令此时就不能少,表示编译的文件与路径无关,也就是说生成的文件都是相对路径,而不是绝对路径,如果使用绝对路径,那么在多个进程共享使用这个库的时候,会根据这个代码段引用的数据对象需要重定位, 重定位会修改代码段的内容,这就造成每个使用这个.so文件代码段的进程在内核里都会生成这个.so文件代码段的copy,那么就相当于每个进程都维护了一个自己的.so的副本,因此就做不到共享了。那么生成的文件是路径无关的,那么不管被加载到内存的任何位置,程序都可以正确的执行。
我们先在lib下建立一个linkname的软链接,然后再去编译main.cpp和共享库,其中-L参数后面跟你的共享库的所在目录,-l后面跟库的名称,因为我的库是libmytest.so,所以我的库的名称就是mytest,需要注意的是这里要有linkname的软链接才可以用-l参数,否则会报cannot find错误(原因就是有.so.文件,但是没有.so文件)。
这里虽然编译成功了,但是其实还是无法运行test,是因为系统找不到这个共享库的路径,可以通过ldd test来查看可执行文件的动态链接库
可以清晰的看出来找不到我们要链接的动态库,所以我们要去修改一下etc/ld.so.conf的配置文件,在最后一行加上你的库所在的目录就好了。然后通过ldconfig -v来更新系统中的动态链接库,然后我们就可以发现soname就有动态库的目录了,而且在lib中也生成了soname的软链接文件。
然后就可以运行我们的可执行文件了。