linux开发:linux内存加载动态库?

1 Linux应用程序可能会使用到两种函数库,一种静态库、一种动态库,静态库以.a为扩展名,动态库以.so为扩展名 。二者都使用广泛 。
2 动态库和静态库的基本概念?
静态库,是在可执行程序连接时就已经加入到执行码中动态链接库可以干嘛 , 在物理上成为执行程序的一部分;使用静态库编译的程序运行时无需该库文件支持,哪里都可以用 , 但是生成的可执行文件较大 。动态库,是在可执行程序启动时加载到执行程序中,可以被多个可执行程序共享使用 。使用动态库编译生成的程序相对较小,但运行时需要库文件支持,如果机器里没有这些库文件就不能运行 。
3 如何使用动态库?
动态库也叫共享库,如果在程序连接时使用共享库 , 就必须在运行时找到共享库的位置 。Linux的可执行程序在执行的时候默认是先搜索/lib和/usr/lib这两个目录,然后按照/etc/ld.so.conf里面的配置搜索绝对路径 。同时,linux也提供了环境变量LD_LIBRARY_PATH供用户选择使用 , 用户可以通过它来查找默认路径之外的其他路径,如要查找/work/lib路径,你可以在/etc/rc.d/rc.local或其他系统启动后即可执行到的脚本文件中添加如下语句:LD_LIBRARY_PATH=/work/lib:$(LD_LIBRARY_PATH) 。并且LD_LIBRARY_PATH路径优先于系统默认路径之前查找 。
不过LD_LIBRARY_PATH的设定作用是全局的 , 过多的使用可能会影响到其他应用程序的运行,所以多用在调试 。
4 库的连接路经和运行路经
现代连接器在处理动态库时将链接时路径(Link-time path)和运行时路径(Run-time path)分开,用户可以通过-L指定连接时库的路径,通过-R(或-rpath)指定程序运行时库的路径,大大提高了库应用的灵活性 。比如我们做嵌入式移植时#arm-linux-gcc $(CFLAGS) –o target –L/work/lib/zlib/ -llibz-1.2.3 (work/lib/zlib下是交叉编译好的zlib库),将target编译好后我们只要把zlib库拷贝到开发板的系统默认路径下即可 。或者通过- rpath(或-R )、LD_LIBRARY_PATH指定查找路径
5 动态库的加载使用?
基本上每个linux程序都至少会用一个动态库,查看某个程序使用了那些动态库,使用命令ldd查看
如:#ldd /bin/ls (查看系统中ls用到的动态库)
#ldd main (查看自定义main用到的动态库)
#ldd -u main(查看自定义main中无用的动态库)
#strace ./main(查看程序启动时加载的所有动态库)
Linux程序启动时加载的库有默认的库也有显式手动连接的库
1 默认:编译代码时不手动连接额外库,那么在代码中使用到库函数时会在运行时自动加载连接需要的库 。
#gcc -o main main.c
#ldd main
linux-vdso.so.1 => (0x00007fffa1b6d000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff4fde52000)
【linux开发:linux内存加载动态库?】/lib64/ld-linux-x86-64.so.2 (0x00007ff4fe230000)
2 手动:编译代码时手动添加额外的库,那么在程序运行时,既要加载默认的库,还要加载手动添加的库 。加载的库多了,会影响程序启动的速度 。
#gcc -o main1 -lm -lrt main.c (编译时手动连接数学库、线程库)
#ldd main1
linux-vdso.so.1 => (0x00007fff88770000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f993a0cc000)
/lib64/ld-linux-x86-64.so.2 (0x00007f993a4aa000)
#strace ./main1
从程序运行中可以看出main1运行时即加载了默认库,也加载了手动指定的库(在此手动指定的数学库、线程库是代码不需要的动态链接库可以干嘛 , 但在程序运行时也加载了),会影响整个程序的加载速度 。
大家知不知道linux从程序(program或对象)变成进程(process或进程),要经过哪些步骤呢 , 简单的说分三步:
1、fork进程,在内核创建进程相关内核项,加载进程可执行文件;
2、查找依赖的so,一一加载映射虚拟地址
3、初始化程序变量 。
可以看到,第二步中dll依赖越多,进程启动越慢 , 并且发布程序的时候,有这些链接但没有使用的so,同样要一起跟着发布,否则进程启动时候,会失败 , 找不到对应的so 。所以我们不能像上面那样,把一些毫无意义的so链接进来,浪费资源 。
6 关于Linux程序连接so有两种方式:隐式和显示
所谓显示就是程序主动调用dlopen打开相关so;首先,dlopen的so使用ldd是查看不到的 。其次,使用dlopen打开的so并不是在进程启动时候加载映射的 , 而是当进程运行到调用dlopen代码地方才加载该so,也就是说,如果每个进程显示链接a.so;但是如果发布该程序时候忘记附带发布该a.so,程序仍然能够正常启动 , 甚至如果运行逻辑没有触发运行到调用dlopen函数代码地方 。该程序还能正常运行,即使没有a.so. 既然显示加载这么多优点,那么为什么实际生产中很少码农使用它呢, 主要原因还是起使用不是很方便 , 需要开发人员多写不少代码 。所以不被大多数码农使用,还有一个重要原因应该是能提前发现错误,在部署的时候就能发现缺少哪些so , 而不是等到实际上限运行的时候才发现缺东少西 。
本文到此结束,希望对大家有所帮助!

猜你喜欢