LINUX 动态链接库 - 学习总结


共享库的定义:

本质上来说库是一种可执行代码的二进制形式(符合ELF文件格式,即不同操作系统之间可移植的二进制文件格式,readelf -l查看),可以被操作系统载入内存执行。

功能上讲库的执行代码可在多进程、线程(运行程序)间共享,节省了空间,提高了效率,具备很高的灵活性。


共享库的分类:

库可分为两种:静态库(.a)和动态库(.so),不同点在于被载入的时刻不同;

静态库的代码在编译过程中已经被载入可执行程序,因此体积较大(用ar命令创建静态库);

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

动态库的代码是在可执行程序运行时才载入内存的,在编译过程中只简单的引用,因此代码体积较小;

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


动态链接库详解:

动态库,即Shared Object,共享对象。动态库的特点是程序运行时可以动态选择加载各种程序模块;优点在于不同的应用程序调用相同的库,那么在内存里只需要有一份该共享库的实例,节省内存。

动态链接:应用程序执行时,操作系统将程序依赖的目标文件加载到内存,如果依赖关系满足,则开始进行链接,即符号解析(符号:编译器在对程序进行编译时生成符号表,例如:函数),地址重定位(重定位:将程序逻辑地址空间转变成内存中实际物理地址空间的过程),假如依赖文件已经被载入内存,直接进行链接。

补充说明:应用程序加载时,动态链接器(共享库装载器ld-***.so)将所有依赖的动态库装载到进程地址空间(虚拟地址空间/逻辑地址空间),将程序中未决议符号绑定到相应的动态链接库,进行重定位工作。

当程序被调用的时候,Linux共享库装载器也自动被调用,作用是保证程序所需要的所有适当版本的库都被调入内存。

当程序隐式或显示调用一个动态库里的函数时,系统都要把这个动态库映射到这个进程的虚拟地址空间里,这使得动态库成为进程的一部分,以这个进程的身份执行,使用这个进程的堆栈。

创建动态链接库使用参数说明:
-fPIC、-shared、-WI

装载时重定位可能导致指令部分无法进程间共享的问题,因为动态库被一个进程重定位成自己使用的值后,其他进程就无法使用该共享对象了,这种装载重定位就是单独使用-shared参数,不使用-fPIC的情况。

-fPIC:意义就在于解决上述情况,基本思想是把指令中需要进行重定位修改的那部分分离出来,跟数据部分一起,使得每个进程都有一个副本,即地址无关代码技术;

-shared:告诉编译器产生共享库代码;

-WI:告诉编译器将后面的参数传递到链接器;

-fPIC和-fpic区别在于-fpic产生的代码小,执行速度快。但是-fpic在某些平台上会有限制,因为地址无关代码是硬件平台相关的。比如全局符号的数量、代码长度等。-fPIC则没有这种限制。readelf -d ***|grep TEXTREL,如果没有输出,则是PIC的,TEXTREL即代码段重定位表,PIC的动态库是不存在TEXTREL的。


动态链接库中的全局变量,在进程间共享,但写时拷贝

写时拷贝(copy-on-write),针对共享资源所应用的一种技术,例:多个进程访问同一个资源时,属性是可读,但不可写,如果需要修改,则copy一份出来,各进程都有自己的备份,故共享,但写时拷贝。POSIX利用写时拷贝来实现fork函数,产生的新进程不是拷贝整个地址空间,而是通过将页面标记为写时复制的方式,与父进程共享这些页面。如果子进程在这些页面中写入数据了,则生成一份进程私有的拷贝。如果没有写操作,则2个进程继续共享页面,不会执行拷贝动作。不管怎么样,内存管理器只拷贝一个进程试图要写入数据的那些页面,而不是整个地址空间。

LINUX下使用动态链接库,源程序需要包含dlfcn.h头文件,此文件定义了调用动态链接库的函数的原型。

1、dlopen

原型void *dlopen(const char *filename,int flag);

dlopen用于打开指定文件filename的动态链接库,并返回操作句柄,失败返回NULL。

filename:如果不是绝对路径,查找顺序是LD_LIBRARY_PATH -> ld.so.cache缓存 -> /lib,/usr/lib。

flag:RTLD_NOW 立刻计算库的依赖性;

    RTLD_LAZY 在需要的时候才计算;

    RTLD_GLABAL 使得那些在以后才加载的库可以获得其中的符号(函数);

dlopen打开一个新库,并把它装入内存。主要用来加载库中的符号,这些符号在编译的时候是不知道的。

2、dlsym

原型void *dlsym(void *handle,char *symbol);

取函数动态执行地址。

handle:dlopen函数返回的句柄;

symbol:所调用函数的符号名称;

int (*symbol)(char *argu,char *argu1);

symbol=(int (*)(char *,char *))dlsym(handle,"symbol");

3、dlclose

原型int dlclose(void *handle);

用于关闭指定句柄的动态链接库,当所要关闭库的使用计数为0时,才会真正被系统卸载;

4、dlerror

原型const char *dlerror(void);

返回值为NULL时,表示操作函数执行成功;


查看方法:

可通过ldd命令查看一个可执行程序依赖的共享库。

ldd /lib64/libpthread.so.*


共享库的识别:

1、安装在/lib、/usr/lib下,系统动态载入器(dynamic linker/loader)ld-linux.so*默认可以找到;

2、编辑/etc/ld.so.conf文件,加入库文件所在目录的路径,运行ldconfig,重建ld.so.cache文件;

3、对于elf格式文件,可以设置环境变量LD_LIBRARY_PATH,将路径包含进去;


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值