很多时候我们需要多次调用一些函数,为了方便我们需要把这写函数封装在一个库里面,以便在以后主函数链接生成可执行文件的时候调用。因为是在主函数连接的时候利用,所以需要打包生成库文件的文件都必须是已经编译过的目标文件(即.o文件)。
静态库:
四个数学函数分别在不同的文件里面,但他们包含在一个头文件下面:
math.h:
#ifndef MATH_H_
#define MATH_H_
#include <stdio.h>
typedef int(* funp)(int ,int );
int add(int x,int y);
int sub(int x,int y);
int mul(int x,int y);
int div(int x,int y);
#enfif
函数文件分别为:
add.c
#include "math.h"
int add(int x,int y)
{
return x+y;
}
其余同理。
主函数:
#include "math.h"
void callback(fun p,int x,int y,char *fname)
{
printf("%s=%d
",fname,p(x,y));
}
int main(void)
{
int a=9,b=3;
callback(add,a,b,"add");
callback(sub,a,b,"sub");
callback(mul,a,b,"mul");
callback(div,a,b,"div");
return 0;
}
现在需要将数学函数都打包生成一个库函数,以便主函数直接调用。
首先编译数学函数,得到目标文件,然后打包生成静态库文件。
步骤如下:
1:文件预处理:gcc -E add.c -o test.i
.i文件中存放着.c预处理之后的代码,如果不加 -o test.i 的话不会输出预处理结果, 看不到.i文件。
2:文件编译:gcc -S add.i -o add.s
预处理之后,可直接对生成的.i文件编译,生成汇编代码:
3:汇编:gcc -c add.s -o add.o
汇编器将汇编代码编译为目标文件.o
/*因为我们的目的是打包生成库函数,是供主函数调用时链接生成可执行文件的,所以只要生成目标文件就可以了。但是主函数要生成可执行文件,除了上面3个步骤外,还需要一个链接的过程。就是将主函数生成的目标文件和附加的目标文件链接起来,最终生成可执行文件。例如:gcc - main.o main */
当生成所有需要的.o文件后,就需要打包生成静态库文件了
在命令行中输入:
ar rs libmymath.a add.o sub.o mul.o div.o
库文件名都是以lib开头的,静态库以.a作为后缀(archive),ar命令类似以tar命令,用来打包的,但是把目标文件打包生成静态库文件只能用ar命令。选项r表示将后面的文件列表添加到文件包.a当中,如果.a不存在就创建,存在就更新。s表示为静态库创建索引,给链接器使用。ranlib也可以为静态可创建索引,上面命令等价于:
ar r libmymath.a add.o sub.o mul.o div.o
ranlib libmymath.a
系统就会提示创建一个静态库文件。这样静态库就做好了。
然后我们编译主函数的时候把库文件链接上:
gcc main.c -L库文件路径 -l库文件名(文件名数除去前后缀的部分,本例中就是mymath) -I头文件路径 -o main
-L选项告诉编译器到哪里寻找库文件,-l告诉编译器要连接哪个库,-I告诉编译器去哪里寻找头文件。注意,即使库文件就在当前目录,编译器也不会去找,所以-L选项不能少。
由于利用指令的方法创建库函数很麻烦,下面介绍一种简单的方法:
用Makefile创建静态库函数
将静态库函数需要包含的目标文件的源文件.c放到一个文件夹当中,在此文件夹中创建Makefile文件(vim Makefile)
#Makefile
libmymath.a:*.c
gcc -c *.c
ar rs libmymath.a *.o
clean:
@echo "cleanning project"
-rm *.o
-rm *.a
@echo "clean completed"
.PHONY :clean
由于Makefile的隐含规则中可以默认当没有源文件时自动生成源文件,所以Makefile可以简化为
#Makefile
libmymath.a:*.o
ar rs libmymath.a *.o
clean:
@echo "cleanning project"
-rm *.o
-rm *.a
@echo "clean completed"
.PHONY :clean
当在当前路径下的命令行中输入make时就可创建静态库函数,同理可以再主函数文件夹下面写一个Makefile,也可以完成。
动态库:
生成动态库也需要目标文件,但是和一般地目标文件有所不同(具体原因待补),在编译时需要加-fPIC选项,表示-f后面编译的选项生成的是位置无关代码。
例如:
gcc -c -fPIC add.c sub.c mul.c div.c
这样生成目标文件了,之在打包生成动态库:
gcc -shared -o libmymath.so *.o
这样就生成了动态库了。
这样可以一步完成:gcc -shared -fPIC -o libmymath.so *.c
现在把main.c和共享库编译连接在一起:
gcc main.c -L库文件位置 -l库文件名 -I头文件位置 -o main
然后运行:
./main
结果出乎意料,编译时没问题,由于指定了-L选项,编译器可以找到库文件,而运行时却说找不到。那么运行时在哪些路径下找共享库呢?可以用ldd命令查看:
ldd mian
共享库路径的搜索顺序:
1,在环境变量LD_LIBRARY_PATH保存的路径中查找。
2,然后从缓存文件/etc/ld.so.cache中查找。
3,如果上述步骤都没找到,则到默认的系统库文件目录中查找,先是/lib然后是/usr/lib。
既然如此,我们在运行程序时修改环境变量,把共享库所在的目录添加到搜索路径:
LD_LIBRARY_PATH=/home/...(库文件的路径) ./main
也可以用这样两条命令:
export LD_LIBRARY_PATH=/home/...(库文件路径,可以用$PWD表示在当前路径)
./main
第一条命令在当前shell进程中设置一个环境变量,一旦在shell进程中设置了环境变量,以后每次执行时shell都会把自己的环境变量传给新创建的进程,所以第二条命令创建的进程main就会获得这个环境变量。但是在关闭shell,重启一遍
terminal时这个新设置的环境变量就无效了。注意这两种执行方式的区别,第一种 LD_LIBRARY_PATH=/home/...(库文件的路径) ./main 命令室友当前创建的main进程才获得这个环境变量,shell本身不保存,以后执行其他命令也不会获得它,所以重新执行./main时找不到共享库,但是第二种方法在不关闭shell的前提下重新执行./main是有效的。
另外一种方法就是把libmymath.so 考到/usr/lib或者/lib目录,这样可以确保动态链接器能找到这个共享库
用Makefile的方法完成共享库的创建和链接:
#makefile
libmymath.so;*.c
gcc -c *.c -fPIC
gcc -shared -o libmymath.so *.o
install:
sudo cp libmymath.so /usr/lib
remove:
sudo rm /usr/lib/libmymath.so
clean:
@echo "clean project"
-rm *.o
@echo "clean completed"
distclean:
@echo "clean project"
-rm libmymath.so
@echo "clean completed"
.PHONY:clean
.PHONY:distclean
利用隐含规则可以简化:
#makefile
CFLAGS=--fPIC
libmymath.so;*.o
gcc -shared -o libmymath.so *.o
install:
sudo cp libmymath.so /usr/lib
remove:
sudo rm /usr/lib/libmymath.so
clean:
@echo "clean project"
-rm *.o
@echo "clean completed"
distclean:
@echo "clean project"
-rm libmymath.so
@echo "clean completed"
.PHONY:clean
.PHONY:distclean
在主函数文件夹里面写下Makefile:
#Makefile
main:mian.o
make -C 动态库文件的路径
gcc main.c -L动态库文件路径 -l动态库文件名 -I头文件路径 --o main
clean:
@echo "clean project"
-rm *.o
@echo "clean completed"
distclean:
@echo "clean project"
-rm main
@echo "clean completed"
这样在在库文件目录下make,然后make install安装完成后,在主函数文件目录下make命令就生成了main可执行文件。然后在库文件目录下make remove即可卸载安装。
1,使用dlopen()打开指定的动态链接库文件,
2,使用dlsym()查找库文件中的函数,并赋值给一个函数指针变量,
3,使用函数指针变量运行对应的函数,
4,使用dlclose关闭打开的动态链接库文件,
编译命令:gcc -o testdlso testdlso.c -ldl
运行: ./testdlso fun.so
http://www.cnblogs.com/Anker/p/3746802.html
dlopen
基本定义
功能:打开一个动态链接库
包含头文件:
#include <dlfcn.h>
函数定义:
void * dlopen( const char * pathname, int mode );
函数描述:
在dlopen的()函数以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程。使用dlclose()来卸载打开的库。
mode:分为这两种
RTLD_LAZY 暂缓决定,等有需要时再解出符号
RTLD_NOW 立即决定,返回前解除所有未决定的符号。
RTLD_LOCAL
RTLD_GLOBAL 允许导出符号
RTLD_GROUP
RTLD_WORLD
返回值:
打开错误返回NULL
成功,返回库引用,指向动态库文件的void指针
编译时候要加入 -ldl (指定dl库)
例如
gcc test.c -o test -ldl
编辑本段
使用 dlopen
dlopen()是一个强大的库函数。该函数将打开一个新库,并把它装入内存。该函数主要用来加载库中的符号,这些符号在编译的时候是不知道的。比如
Apache Web
服务器利用这个函数在运行过程中加载模块,这为它提供了额外的能力。一个配置文件控制了加载模块的过程。这种机制使得在系统中添加或者删除一个模块时,都
不需要重新编译了。
可以在自己的程序中使用 dlopen()。dlopen() 在 dlfcn.h 中定义,并在 dl
库中实现。它需要两个参数:一个文件名和一个标志。文件名可以是我们学习过的库中的 soname。标志指明是否立刻计算库的依赖性。如果设置为
RTLD_NOW 的话,则立刻计算;如果设置的是 RTLD_LAZY,则在需要的时候才计算。另外,可以指定
RTLD_GLOBAL,它使得那些在以后才加载的库可以获得其中的符号。
当库被装入后,可以把 dlopen() 返回的句柄作为给 dlsym() 的第一个参数,以获得符号在库中的地址。使用这个地址,就可以获得库中特定函数的指针,并且调用装载库中的相应函数。
NOTE: 如果pathname是NULL, 则返回的是调用dlopen的模块的库引用(handle)。 例如: 如果我们在我们的APP里面调用dlopen去动态load一个.so, 但是pathname传入的是NULL, 则dlopen最后返回的应该是该APP的引用(handle)
Additional link: http://tldp.org/HOWTO/Program-Library-HOWTO/dl-libraries.html
--------------------------------------------------------------------------------------------------------------------------
dlsym
dlsym()的函数原型是
void* dlsym(void* handle,const char* symbol)
该函数在<dlfcn.h>文件中。
handle是由dlopen打开动态链接库后返回的指针,symbol就是要求获取的函数的名称,函数返回值是void*,指向函数的地址,供调用使用
取动态对象地址:
#include <dlfcn.h>
void *dlsym(void *pHandle, char *symbol);
dlsym根据动态链接库操作句柄(pHandle)与符号(symbol),返回符号对应的地址。
使用这个函数不但可以获取函数地址,也可以获取变量地址。比如,假设在so中
定义了一个void mytest()函数,那在使用so时先声明一个函数指针:
void (*pMytest)(),然后使用dlsym函数将函数指针pMytest指向mytest函数,
pMytest = (void (*)())dlsym(pHandle, "mytest");
--------------------------------------------------------------------------------------------------------------------------
dlclose
dlclose()
包含头文件:
#include <dlfcn.h>
函数原型为:
int dlclose (void *handle);
函数描述:
dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。
--------------------------------------------------------------------------------------------------------------------------
dlerror
dlerror()
包含头文件:
#include <dlfcn.h>
函数原型:
const char *dlerror(void);
函数描述:
当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。
LINUX创建与使用动态链接库并不是一件难事。
编译函数源程序时选用-shared选项即可创建动态链接库,注意应以.so后缀命名,最好放到公用库目录(如/lib,/usr/lib等)下面,并要写好用户接口文件,以便其它用户共享。
使用动态链接库,源程序中要包含dlfcn.h头文件,写程序时注意dlopen等函数的正确调用,编译时要采用-rdynamic选项与-ldl选项 ,以产生可调用动态链接库的执行代码
EXAMPLE
#include <stdio.h>
int add(int a, int b)
{
return a+b;
}
int sub(int a, int b)
{
return a-b;
}
#include <stdio.h>
#include <dlfcn.h>
typedef int (* funp)(int ,int );
int main(void)
{
void *handle; /*用来标定库文件加载的内存位置*/
handle = dlopen("./libmath.so", RTLD_LAZY); /*根据库文件的路径加载库文件到内存中*/
if(!handle){
dlerror();
}
funp fp = (int (*)(int ,int ))dlsym(handle, "add"); /*根据函数名符号找到需要动态加载的函数位置*/
if(!fp){
dlerror();
}
printf("add = %d
",fp(3,4));
if(dlclose(handle)){ /*从内存中卸载动态库文件*/
dlerror();
}
return 0;
}
编译:gcc main.c -ldl
注意:事先要把动态库文件准备好。