动态库技术一般能减小程序的大小,节省空间,提升效率,具备很高的灵活性,对于升级软件版本也更加容易。与静态库不一样,动态库里面的函数不是执行程序自己 的一部分,而是在程序执行时按需载入,其执行代码能够同时在多个程序中共享。因为在编译过程当中没法知道动态库函数的地址,因此须要在运行期间查找,这对程 序的性能会有影响。 程序员
对于共享库来说,它只包括2个段:只读的代码段 和可修改的数据段。堆和栈段,只有进程才有。若是你在共享库的函数里,分配了一块内存,这段内存将被算在调用该函数的进程的堆中。代码段因为其内容是对每 个进程都是同样的,因此它在系统中是惟一的,系统只为其分配一块内存,多个进程之间共享。数据段因为其内容对每一个进程是不同的,因此在连接到进程空间 后,系统会为每一个进程建立相应的数据段。也就是说若是一个共享库被N个进程连接,当这N个进程同时运行时,同时共享一个代码段,每一个进程拥有一个数据段,系统中共有N个数据段。PIC即position independent code,使.so文件的代码段变为真正意义上的共享。若是编译时不加-fPIC,则加载.so文件的代码段时,代码段引用的数据对象须要重定位, 重定位会修改代码段的内容,这就形成每一个使用这个.so文件代码段的进程在内核里都会生成这个.so文件代码段的copy。 函数
1. 静态加载 性能
在程序编译的时候加上“-l”选项,指定其所依赖的动态库,这个库的名字将记录在ELF文件的.dynamic节。在程序运行时,loader会预先将程序所依赖的全部动态库都加载在进程空间中。 编码
优势:动态库的接口调用简单,能够直接调用。 spa
缺点:动态库的生存周期等于进程的生存周期,其加载时机不灵活。 code
2. 动态加载 orm
在程序中编码来指定加载动态库的时机,常常使用的函数dlopen和dlclose。 htm
优势:动态库加载的时机很是灵活,能够很是细致的定义动态库的生存周期。 对象
缺点:动态库的接口调用起来比较麻烦,同时还要关注动态库的生存周期。 接口
=====================================================
#include <dlfcn.h>
void * dlopen( const char * pathname, int mode );
函数描述:
在dlopen的()函数以指定模式打开指定的动态链接库文件,并返回一个句柄给调用进程。使用dlclose()来卸载打开的库。
mode:分为这两种
RTLD_LAZY 暂缓决定,等有须要时再解出符号
RTLD_NOW 当即决定,返回前解除全部未决定的符号。
RTLD_LOCAL 不容许导出符号
RTLD_GLOBAL 容许导出符号
RTLD_NODELETE
RTLD_NOLOAD
RTLD_DEEPBIND //具体含义可经过man查看dlopen函数说明
返回值:
打开错误返回NULL
成功,返回库引用
编译时候要加入 -ldl (指定dl库)
int dlclose (void *handle);
函数描述:
dlclose用于关闭指定句柄的动态连接库,只有当此动态连接库的使用计数为0时,才会真正被系统卸载。
在dlopen一个共享库时,
a、进程会加载该共享库的代码段和数据段,同时为这个共享库计数加1。
b、进程查找该共享库的dynamic节,查看其所依赖的共享库。
c、首先检查所依赖库是否已经被加载,若是已被加载,则为这个共享库计数加1。若是未被加载,则加载其代码段和数据段,而后为这个共享库计数加1。
d、再查找这些库所依赖的库。最终进程会为每一个加载的共享库维护一个依赖的计数。
在dlclose共享库时:
a、首先将该共享库的计数减1,若是该共享库依赖计数为0,则卸载该共享库。
b、在dynamic节中,查找其所依赖的共享库。
c、为每一个共享库的计数减1,若是该共享库依赖计数为0,则卸载该共享库。
d、重复上面的步骤。
a、能够在程序启动的时候,减小加载库的数量,这样能够加快进程的启动速度和减小加载库的内存使用。
b、为进程提供了卸载共享库的机会,这样就能够回收共享库代码段和数据段所占用的内存。
对于程序员编码来说,会产生必定的疑惑。一个static的对象的生存周期是贯穿在进程始终的,实际上不是这样。在动态库中的static对象,其生命周期等于该动态库的生命周期。采用静态连接的方式,动态库的生命周期等于进程的生命周期;而采用动态加载的方式,则是不一样的。
参考:
《嵌入式Linux内存与性能详解》