1 什么是库?
库,就是是源程序文件编译后生成的目标文件
2 为什么要有库?
一个项目中有很多.c文件,比如一个函数可能就是一个源文件,对这样的.c编译后会有很多.o文件,不方便发布,可以把这些.o做到一个起形成库文件,这样别人就可以链接这个库进行使用。
3 库在linux中分两种:
静态对象库(静态库)
库文件以lib开头,以.a结束,中间是库名。
静态库在程序编译时会被连接到目标代码中,目标程序运行时将不再需要该动态库,移植方便,体积较大,但是浪费空间和资源,因为所有相关的对象文件与牵涉到的库被链接合成一个可执行文件。
共享对象库(windows下称为动态链接库)
动态库在程序编译时并不会被连接到目标代码中,而是在程序运行时才被载入,因此体积较小,可以实现进程间的资源共享,甚至可以真正做到链接载入完全由程序员在程序代码中控制,另外将一些程序的升级变得简单,但是在程序运行时需要动态库存在
4 创建静态库的过程
1. 写源程序 xxx.c
比如在当前目录下创建一个lib文件夹,然后写个打印函数
cat print.c
cat print.c
#include <stdio.h> void print(double d){ printf("d=%.2lf\n", d); }
和一个加减法的函数
cat mymath.c
double add(double a, double b){ return a+b; } double sub(double a, double b){ return a-b; }
2. 编译源程序, 生成.o 文件
gcc -c print.c
gcc -c mymath.c
3. 使用ar打包工具生成静态库
ar –r libyyy.a xxx1.o xxx2.o ….
注:
ar -- create and maintain library archives
-r -- Replace or add the specified files to the archive.
其中yyy是库名, xxx1.o,xxx2.o...是要归档的目标文件名
我们对上面的两个.o生成的目标文件创建静态库
ar -r libmymath.a mymath.o print.o
ar: creating archive libmymath.a
4. 提供头文件,方便调用(一般情况下提供一个库文件的同时会提供一个头文件,因为调用库里面某个函数时要声明,一般把这些函数的声明放在一个头文件中,方便别人调用)
#ifndef MYMATH_H #define MYMATH_H //add double add(double a, double b); //sub double sub(double a, double b); //print void print(double d); #endif
这时候一个静态库就创建完成了,现在看下如何使用这个静态库文件
使用静态库的过程
1. 写调用源程序xxx.c (需要包含所用到库的头文件)
cat mytest.c
#include "mymath.h"
int main(){
double r = add(10, 20);
print(r);
r = sub(3.14, 2.08);
print(r);
return 0;
}
2. 编译源程序,生成.o文件
gcc -c mytest.c
3. 链接静态库
gcc xxx.o –l xxx –L path
-l 后面是要连接的库名(不是文件名,是去掉lib 和.a)
-L 是库文件所在的路径 (如果提供环境变量LIBRARY_PATH,可以省略-L选项)
这里我们利用来链接
gcc mytest.o -l mymath -L .
直接运行得到
./a.out
d=30.00
d=1.06
5 共享对象库
创建共享库的过程如下:
1. 写源程序 xxx.c
2. 编译源程序,用-fpic生成.o 文件
gcc -c -fpic xxx.c
3. 生成共享库文件
gcc --shared xxx.o -o libxxx.so
(用gcc,而不是生成静态库的ar, 注意共享库的库名以lib开头,以.so结尾)
4. 提供头文件,方便调用
使用共享库的过程:
1. 写调用源程序 xxx.c
2. 编译源程序,生成.o 文件
3. 连接共享库文件
gcc xxx.o -l xxx -L path
如果提供环境变量LIBRARY_PATH, 可以省略-L选项
4 执行过程中,动态加载共享库,系统会自动查找LD_LIBRARY_PATH环境变量中的路径,以确定库文件的位置,如果找到,会把它加载到内存中执行,如果找不到,程序会执行失败, 这点和静态库是不一样的。
下面还以上面创建静态库的几个函数为例,介绍共享库的创建和使用
创建共享库
1 把mymath.c print.c mytest.c mymath.h拷贝到slib目录
2 gcc -c -fpic mymath.c print.c //编译
3 gcc -shared mymath.o print.o -o libmymath.so //生成共享库,这时发现在当前目录有一个以绿色显示的,有执行权限的libmymath.so的库文件
使用共享库:
1 拷贝源文件mytest.c
2 gcc -c mytest.c
3 gcc mytest.o -l mymath -L .
4 a.out
如果执行出错,
a.out: error while loading shared libraries: libmymath.so: cannot open shared object file: No such file or directory
原因可能是LD_LIBRARY_PATH 中没有当前目录,这时需要配置一下就好了
export LD_LIBRARY_PATH=. (根据实际情况来配置)
./a.out
d=30.00
d=1.06
linux上用ldd a.out 可以看到a.out在运行时需要的共享库等资源, 在mac中可以用otools来查看,例如
otool -L a.out
a.out:
libmymath.so (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1238.0.0)
6 动态加载库
动态库也是在程序运行时载入,但与共享库不同的是,动态库使用的库函数不是在程序运行使开始载入,而是在程序中的语句需要使用该函数时才载入。动态库可以在程序运行期间释放动态库所占用的内存,腾出空间供其他程序使用。
有专门的一组API用于完成打开动态库,查找符号,处理出错,关闭动态库等功能。
下面对这些接口函数逐一介绍,用这几个函数需要: #include <dlfcn.h>
(1) dlopen
函数原型:void *dlopen(const char *libname,int flag);
功能描述:dlopen必须在dlerror,dlsym和dlclose之前调用,表示要将库装载到内存,准备使用。
如果要装载的库依赖于其它库,必须首先装载依赖库。如果dlopen操作失败,返回NULL值;如果库已经被装载过,则dlopen会返回同样的句柄。
参数中的libname一般是库的全路径,这样dlopen会直接装载该文件;如果只是指定了库名称,在dlopen会按照下面的机制去搜寻:
a.根据环境变量LD_LIBRARY_PATH查找
b.根据/etc/ld.so.cache查找
c.查找依次在/lib和/usr/lib目录查找。
flag参数表示处理未定义函数的方式,可以使用RTLD_LAZY或RTLD_NOW。RTLD_LAZY表示暂时不去处理未定义函数,先把库装载到内存,等用到没定义的函数再说;RTLD_NOW表示马上检查是否存在未定义的函数,若存在,则dlopen以失败告终。
(2) dlerror
函数原型:char *dlerror(void);
功能描述:dlerror可以获得最近一次dlopen,dlsym或dlclose操作的错误信息,返回NULL表示无错误。dlerror在返回错误信息的同时,也会清除错误信息。
(3) dlsym
函数原型:void *dlsym(void *handle,const char *symbol);
功能描述:在dlopen之后,库被装载到内存。dlsym可以获得指定函数(symbol)在内存中的位置(指针)。
如果找不到指定函数,则dlsym会返回NULL值。但判断函数是否存在最好的方法是使用dlerror函数,
(4) dlclose
函数原型:int dlclose(void *);
功能描述:将已经装载的库句柄减一,如果句柄减至零,则该库会被卸载。如果存在析构函数,则在dlclose之后,析构函数会被调用。
还以上面共享库的例子来说明怎么使用动态库。
在slib中有一个libmymath.so, 现在创建一个testdll.c来动态加载使用它。
cat testdll.c
#include <stdio.h> #include <dlfcn.h> int main(){ // 1.open void* handle = dlopen("./slib/libmymath.so", RTLD_LAZY); // 2.error? char* error = dlerror(); if(error){ printf("Open library failed:%s\n", error); return -1; } // 3.get function double (*f)(double, double) = dlsym(handle, "add"); // 4.invoke function double r = f(1.23, 2.34); printf("r=%.2lf\n", r); // 5.close dlclose(handle); return 0; }
编译连接:
gcc testdll.c -l dl
dlsym, dlopen 等几个函数定义在libdl.so中,如果不指定-l dl,会出错 "undefined reference to ‘dlopen‘ "
部分参考: http://www.cnblogs.com/ThinkingWorld/articles/1861249.html
原文:http://www.cnblogs.com/happyorange/p/6538268.html