动态库和静态库(共享库)
Linux下有两种库:动态库和静态库(共享库)
二者的不同点在于代码被载入的时刻不同。
静态库的代码在编译过程中已经被载入可执行程序,因此体积比较大。
动态库(共享库)的代码在可执行程序运行时才载入内存,在编译过程中仅简单的引用,因此代码体积比较小。
不同的应用程序如果调用相同的库,那么在内存中只需要有一份该动态库(共享库)的实例。
静态库和动态库的最大区别,静态情况下,把库直接加载到程序中,而动态库链接的时候,它只是保留接口,将动态库与程序代码独立,这样就可以提高代码的可复用度,和降低程序的耦合度。
静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。
动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在
source
Add.c
int Add(int a, int b){
return a + b;
}
Sub.c
int Sub(int a, int b){
return a - b;
}
Main.c
#include <stdio.h>
int Add(int, int);
int Sub(int, int);
int main(int argc, char** argv){
printf("%d\r\n", Add(argc, 1));
printf("%d\r\n", Sub(argc, 1));
return 0;
}
无论静态库,还是动态库,都是由.o文件创建的。因此,我们必须将源程序hello.c通过gcc先编译成.o文件。
一 编译:
D:\android-ndk-r12b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin/arm-linux-androideabi-gcc -ID:\android-ndk-r12b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/lib/gcc/arm-linux-androideabi/4.9.x\include-fixed -ID:\android-ndk-r12b/platforms/android-14/arch-arm/usr/include -LD:\android-ndk-r12b/platforms/android-14/arch-arm/usr/lib -nostdlib -lgcc -Bdynamic -lc -c Add.c -o Add.o
D:\android-ndk-r12b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin/arm-linux-androideabi-gcc -ID:\android-ndk-r12b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/lib/gcc/arm-linux-androideabi/4.9.x\include-fixed -ID:\android-ndk-r12b/platforms/android-14/arch-arm/usr/include -LD:\android-ndk-r12b/platforms/android-14/arch-arm/usr/lib -nostdlib -lgcc -Bdynamic -lc -c Sub.c -o Sub.o
D:\android-ndk-r12b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin/arm-linux-androideabi-gcc -ID:\android-ndk-r12b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/lib/gcc/arm-linux-androideabi/4.9.x\include-fixed -ID:\android-ndk-r12b/platforms/android-14/arch-arm/usr/include -LD:\android-ndk-r12b/platforms/android-14/arch-arm/usr/lib -nostdlib -lgcc -Bdynamic -lc -c Main.c -o Main.o
二 静态库
这类库的名字一般是libxxx.a;利用静态函数库编译成的文件比较大,因为整个 函数库的所有数据都会被整合进目标代码中,他的优点就显而易见了,即编译后的执行程序不需要外部的函数库支持,因为所有使用的函数都已经被编译进去了。当然这也会成为他的缺点,因为如果静态函数库改变了,那么你的程序必须重新编译。
静态库的代码在编译时链接到应用程序中,因此编译时库文件必须存在,并且需要通过“-L”参数传递给编译器,应用程序在开始执行时,库函数代码将随程序一起调入进程内存段直到进程结束,其执行过程不需要原静态库存在。
在UNIX中,使用ar命令创建或者操作静态库
ar archivefile objfile
archivefile:archivefile是静态库的名称
objfile:objfile是已.o为扩展名的中间目标文件名,可以多个并列
参数 | 意义 |
---|---|
-r | 将objfile文件插入静态库尾或者替换静态库中同名文件 |
-x | 从静态库文件中抽取文件objfile |
-t | 打印静态库的成员文件列表 |
-d | 从静态库中删除文件objfile |
-s | 重置静态库文件索引 |
-v | 创建文件冗余信息 |
-c | 创建静态库文件 |
1. 打包成lib:
D:\android-ndk-r12b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin/arm-linux-androideabi-ar r Add_Sub.a Add.o Sub.o
D:\android-ndk-r12b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin/arm-linux-androideabi-ar: creating Add_Sub.a
2. 链接:
D:\android-ndk-r12b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin/arm-linux-androideabi-gcc -ID:\android-ndk-r12b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/lib/gcc/arm-linux-androideabi/4.9.x\include-fixed -ID:\android-ndk-r12b/platforms/android-14/arch-arm/usr/include -LD:\android-ndk-r12b/platforms/android-14/arch-arm/usr/lib -nostdlib -lgcc -Bdynamic -lc Main.o D:\android-ndk-r12b/platforms/android-14/arch-arm/usr/lib/crtbegin_dynamic.o D:\android-ndk-r12b/platforms/android-14/arch-arm/usr/lib/crtend_android.o Add_Sub.a -o Main
3. 执行
adb push Main /data/local/tmp
adb shell chmod 755 /data/local/tmp/Main
adb shell /data/local/tmp/Main
三 动态库
这类库的名字一般是libxxx.so
;相对于静态函数库,动态函数库在编译的时候 并没有被编译进目标代码中,你的程序执行到相关函数时才调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。由于函数库没有被整合进你的程序,而是程序运行时动态的申请并调用,所以程序的运行环境中必须提供相应的库。动态函数库的改变并不影响你的程序,所以动态函数库的升级比较方便
不同的UNIX系统,链接动态库方法,实现细节不一样
编译PIC型.o中间文件的方法一般是采用C语言编译器的-KPIC或者-fpic选项,有的UNIX版本C语言编译器默认带上了PIC标准.创建最终动态库的方法一般采用C语言编译器的-G或者-shared选项,或者直接使用工具ld创建。
最主要的是GCC命令行的一个选项:
-shared 该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件
-fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。
-L.:表示要连接的库在当前目录中
-ltest:编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称
LD_LIBRARY_PATH:这个环境变量指示动态连接器可以装载动态库的路径。
1. 生成so
D:\android-ndk-r12b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin/arm-linux-androideabi-gcc -ID:\android-ndk-r12b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/lib/gcc/arm-linux-androideabi/4.9.x\include-fixed -ID:\android-ndk-r12b/platforms/android-14/arch-arm/usr/include -LD:\android-ndk-r12b/platforms/android-14/arch-arm/usr/lib -nostdlib -lgcc -Bdynamic -lc -fPIC -c Add.c Sub.c Main.c
D:\android-ndk-r12b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin/arm-linux-androideabi-gcc -shared -ID:\android-ndk-r12b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/lib/gcc/arm-linux-androideabi/4.9.x\include-fixed -ID:\android-ndk-r12b/platforms/android-14/arch-arm/usr/include -LD:\android-ndk-r12b/platforms/android-14/arch-arm/usr/lib -nostdlib -lgcc -Bdynamic -lc Add.o Sub.o -o Add_Sub.so
D:\android-ndk-r12b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin/arm-linux-androideabi-gcc -ID:\android-ndk-r12b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/lib/gcc/arm-linux-androideabi/4.9.x\include-fixed -ID:\android-ndk-r12b/platforms/android-14/arch-arm/usr/include -LD:\android-ndk-r12b/platforms/android-14/arch-arm/usr/lib -nostdlib -lgcc -Bdynamic -lc Main.o D:\android-ndk-r12b/platforms/android-14/arch-arm/usr/lib/crtbegin_dynamic.o D:\android-ndk-r12b/platforms/android-14/arch-arm/usr/lib/crtend_android.o Add_Sub.so -o Main
2. 隐式使用so
D:\android-ndk-r12b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin/arm-linux-androideabi-gcc -ID:\android-ndk-r12b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/lib/gcc/arm-linux-androideabi/4.9.x\include-fixed -ID:\android-ndk-r12b/platforms/android-14/arch-arm/usr/include -LD:\android-ndk-r12b/platforms/android-14/arch-arm/usr/lib -nostdlib -lgcc -Bdynamic -lc -c Main.c -o Main.o
D:\android-ndk-r12b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin/arm-linux-androideabi-gcc -ID:\android-ndk-r12b/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/lib/gcc/arm-linux-androideabi/4.9.x\include-fixed -ID:\android-ndk-r12b/platforms/android-14/arch-arm/usr/include -LD:\android-ndk-r12b/platforms/android-14/arch-arm/usr/lib -nostdlib -lgcc -Bdynamic -lc Main.o D:\android-ndk-r12b/platforms/android-14/arch-arm/usr/lib/crtbegin_dynamic.o D:\android-ndk-r12b/platforms/android-14/arch-arm/usr/lib/crtend_android.o Add_Sub.so -o Main
3. 执行:
adb push Main Add_Sub.so /data/local/tmp
adb shell chmod 755 /data/local/tmp/Main
adb shell chmod 755 /data/local/tmp/Add_Sub.so
adb shell cp /data/local/tmp/Add_Sub.so /system/lib
adb shell /data/local/tmp/Main
动态链接库取代静态库的好处之一就是可以随时升级库的内容。
当动态库被接口完全相同的库文件取代后,可执行程序能迅速的切换到新动态库中代码,省去了编译的麻烦。
4. 显示调用动态库
显示调用动态库,编译时无需库文件,执行时动态可存储于任意位置,库里共享对象必须先申请后使用,不同动态库版本,只要其共享对象接口相同,就可以直接动态加载。
//打开动态库
#include<dlfcn.h>
void *dlopen(const char * pathname,int mode);
//获取动态库对象地址
include<dlfcn.h>
void *dlsym(void *handle,const char *name);
//错误检测
include<dlfcn.h>
char *dlerror(vid);
//关闭动态库
include<dlfcn.h>
int dlclose(void * handle);
动态库的加载或多或少会占用一定的系统资源,比如内存等。因此当不需要或者一段时间内不需要共享动态库时就要卸载之。函数dlclose关闭参数handle所指向的动态库,卸载其所占的内存等资源,此调用后参数handle无效。
实际上,由于动态库可能同时被多个进程共享,当一个进程指向dlclose时,资源并不马上被卸载,只有当全部进程都宣布关闭动态库后,操作系统才开始回收动态库资源。
总结:
编译静态库时先使用-c选项,再利用ar工具产生.编译动态库的方式依不同版本的UNXI而定。隐式调用动态库与静态库的用法相一致,而显示调用动态库则需要借助动态加载共享库函数族。
隐式调用动态库和静态库使用方法一致,使用静态库和使用动态库编译成目标程序使用的gcc命令完全一样,那当静态库和动态库同名时,gcc命令会使用哪个库文件呢?
通过测试可以发现,当静态库和动态库同名时, gcc命令将优先使用动态库.为了确保使用的是静态库, 编译时可以加上 -static 选项,因此多第三方程序为了确保在没有相应动态库时运行正常,喜欢在编译最后应用程序时加入-static
完整的makefile文件
#文件名称
MODALE_NAME = Main
#NDK根目录
NDK_ROOT = D:\android-ndk-r12b
#编译器根目录
TOOLCHAINS_ROOT=$(NDK_ROOT)/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64
#编译器目录
TOOLCHAINS_PREFIX=$(TOOLCHAINS_ROOT)/bin/arm-linux-androideabi
#头文件搜索路径
TOOLCHAINS_INCLUDE=$(TOOLCHAINS_ROOT)/lib/gcc/arm-linux-androideabi/4.9.x\include-fixed
#SDK根目录
PLATFROM_ROOT=$(NDK_ROOT)/platforms/android-14/arch-arm
#sdk头文件搜索路径
PLATFROM_INCLUDE=$(PLATFROM_ROOT)/usr/include
#sdk库文件搜索路径
PLATFROM_LIB=$(PLATFROM_ROOT)/usr/lib
#编译选项
FLAGS=-I$(TOOLCHAINS_INCLUDE) \
-I$(PLATFROM_INCLUDE) \
-L$(PLATFROM_LIB) \
-nostdlib \
-lgcc \
-Bdynamic \
-lc
#所有obj文件
OBJS=$(MODALE_NAME).o \
$(PLATFROM_LIB)/crtbegin_dynamic.o \
$(PLATFROM_LIB)/crtend_android.o
#编译器链接
all:
$(TOOLCHAINS_PREFIX)-gcc $(FLAGS) -c Add.c -o Add.o
$(TOOLCHAINS_PREFIX)-gcc $(FLAGS) -c Sub.c -o Sub.o
$(TOOLCHAINS_PREFIX)-gcc $(FLAGS) -c $(MODALE_NAME).c -o $(MODALE_NAME).o
#打包为lib静态库
lib:
$(TOOLCHAINS_PREFIX)-ar r Add_Sub.a Add.o Sub.o
#链接为可执行程序
link:
$(TOOLCHAINS_PREFIX)-gcc $(FLAGS) $(OBJS) Add_Sub.a -o $(MODALE_NAME)
#编译为动态库
dll:
$(TOOLCHAINS_PREFIX)-gcc $(FLAGS) -fPIC -c Add.c Sub.c Main.c
$(TOOLCHAINS_PREFIX)-gcc -shared $(FLAGS) Add.o Sub.o -o Add_Sub.so
$(TOOLCHAINS_PREFIX)-gcc $(FLAGS) $(OBJS) Add_Sub.so -o $(MODALE_NAME)
#隐式使用动态库
usedll:
$(TOOLCHAINS_PREFIX)-gcc $(FLAGS) -c $(MODALE_NAME).c -o $(MODALE_NAME).o
$(TOOLCHAINS_PREFIX)-gcc $(FLAGS) $(OBJS) Add_Sub.so -o $(MODALE_NAME)
#安装程序到手机
install:
adb push $(MODALE_NAME) /data/local/tmp
adb shell chmod 755 /data/local/tmp/$(MODALE_NAME)
adb shell /data/local/tmp/$(MODALE_NAME)
#安装so到手机
installSo:
adb push Add_Sub.so /data/local/tmp
adb shell chmod 755 /data/local/tmp/Add_Sub.so
adb shell cp /data/local/tmp/Add_Sub.so /system/lib
#运行程序
run:
adb shell /data/local/tmp/$(MODALE_NAME)