C语言动态库

C/C++编译的过程:

预处理,展开头文件,宏定义,条件编译处理等。通过gcc -E source.c -o source.i 或是cppsource.c生成

 

编译。这里是一个下一的编译意义,指的是将预处理后的文件翻译成汇编代码的过程。通过gcc -S source.i生成,默认生成source.s文件。

 

汇编。汇编即将上一步生成的汇编代码翻译成对应的二进制机器码的过程。通过gcc -c source.s来生成source.o文件

 

链接。链接是将生成目标文件和其引用的各种符号等生成一个完整的可执行程序的过程。链接的时候会进行虚拟内存的重定向操作。

编译文件介绍:

.o文件,即目标文件,一般通过.c或是.cpp文件编译而来,相当于VC编译出来的obj文件

.so文件,shared object共享库(对象),相当于windows下的dll

.a文件,archive归档包,即静态库。其实质是多个.o文件打包的结果,相当于VC下的.lib文件

.la文件,libtool archive文件,是libtool自动生成的共享库文件

动态链接库

动态链接库是动态库的一种(先这么区分吧,因为静态库也能动态加载),我们也习惯叫它共享库(Shared Library),当程序加载进内存的时候,动态加载库也会跟着被加载进内存。当动态加载库加载到内存之后,如果后面的程序也起来了,而且也依赖这个动态加载库的话,就不会重复加载。

动态链接库相对于静态库来说更加灵活和复杂,因为在实际应用的时候对动态链接库会有以下要求:

  • 更新动态库之后,依然需要支持那些需要依赖旧版本动态库的程序的正常运行。
  • 当程序运行的时候,需要允许覆盖特定的库,甚至特定的函数。
  • 在程序使用现有库运行时,依然能够需要支持以上两点。

为了达到以上目的,业界制定了一套规范,这套规范主要从两个方面着手:

  • 命名规范。一个动态库会有不同的名字,他们分别起到了不同的作用。
  • 路径规范。一个动态库要放在特定路径下,内核才能够在加载的时候去这个特定路径找到这个动态链接库。

命名规范

一个库有三个分别起到不同作用的名字:soname,real name,link name。

soname

其实就是Shared Object NAME,这个名字的规范就是lib+库名+so+大版本号,它是用来标示动态链接库的主版本的,用于给内核加载动态库选择版本时提供参考。

例如:库名叫做ssl,然后当前版本号是1,那么它的soname就应该是 libssl.so.1

real name

命名规范就是soname+小版本号。如libssl.so.1为例,它的real name就是soname再加小版本号,可以是libssl.so.1.0或者libssl.so.1.1这样。

我们在build一个可执行文件的时候,需要在可执行文件里面记录这个可执行文件依赖于哪些动态库,这样内核在加载可执行文件的时候,才知道有哪些动态库需要加载。在写这条编译命令的时候,是不需要带版本号的。

如soname是libssl.so.1的库子,它的link name就是libssl.so。编译时可以加上"-lssl",当然如果该库路径不在默认编译搜索路径下还需要加上链接该库的路径信息,加入该库位于当前路径下,那么加上"-L."。

库命名使用规范

一般而言,如果你更新的动态库内容变化并没有添加或删除API,只是修改了API的实现,那么soname可以不用变,只要改变你的动态库的real name就好。然后把对应soname做符号链接到你的新版本动态库中,下次启动这个可执行文件时,内核就会加载到最新的动态库了。

如果你更新的动态库里面有添加新的API,可以再在soname后面添加一个小版本号,soname就可以变成libssl.so.1.1。即使原来的可执行文件还是依赖于libssl.so.1,那也不影响使用。

如果你更新的动态库已经不兼容旧版本了,那么soname后面的数字就要改改了,比如改成libssl.so.2。

所以你一定要给你的动态库在编译的时候设置soname,不然soname就跟着文件名走了,到后面涉及版本管理的时候你就坑了。

二者的不同点在于代码被载入的时刻不同。

-  静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库,因此体积较大。

-  动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在,因此代码体积较小。

动态库的好处是,不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。带来好处的同时,也会有问题!如经典的DLL Hell问题,关于如何规避动态库管理问题,可以参考库命名使用规范。

nm命令

有时候可能需要查看一个库中到底有哪些函数,nm命令可以打印出库中的涉及到的所有符号。库既可以是静态的也可以是动态的。nm列出的符号有很多,常见的有三种:

-  一种是在库中被调用,但并没有在库中定义(表明需要其他库支持),用U表示;

-  一种是库中定义的函数,用T表示,这是最常见的;

-  一种是所谓的弱态"符号,它们虽然在库中被定义,但是可能被其他库中的同名符号覆盖,用W表示。

$nm libhello.h

ldd命令

ldd命令可以查看一个可执行程序依赖的共享库.

posted @ 2023-11-01 16:29  PKICA  阅读(50)  评论(0编辑  收藏  举报