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)
/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,而不是等到实际上限运行的时候才发现缺东少西。
本文到此结束,希望对大家有所帮助!