linux环境下,库文件包含静态函数库和动态函数库两种:
静态函数库:
这类库的名字一般是libxxx.a;利用静态函数库编译成的文件比较大,因为整个函数库的所有数据都会被整合进目标代码中,他的优点就显而易见了,即编译后的执行程序不需要外部的函数库支持,因为所有使用的函数都已经被编译进可执行文件了。当然这也会成为他的缺点,因为如果静态函数库改变了,那么你的程序必须重新编译,而且体积也较大。
动态函数库:
这类库的名字一般是libxxx.so,动态库又称共享库;相对于静态函数库,动态函数库在编译的时候并没有被编译进目标代码中,你的程序执行到相关函数时才调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。由于函数库没有被整合进你的程序,而是程序运行时动态的申请并调用,所以程序的运行环境中必须提供相应的库。动态函数库的改变并不影响你的程序,所以动态函数库的升级比较方便。而且如果多个应用程序都要使用同一函数库,动态库就非常适合,可以减小应用程序的体积。
在开发过程中引入库的作用主要包括:
1.函数模块的功能相同,实现代码也相同,通过对它们进行封装为库,方便模块之间的调用,避免代码重复。
2.封装另外一个目的是针对接口编程,对实现代码进行保密,利于代码保护和代码升级。
第一节:静态函数库
在linux中,静态函数库有固定的命名规则:lib+类库名称+.a。
把函数打包成.a静态库包括下面几个步骤:
1.程序实例:
/*************************************************************
FileName : myAddLib.h
FileFunc : 定义头文件
Version : V0.1
Author : Sunrier
Date : 2012-04-28
Descp : Linux下实现静态库
*************************************************************/
#ifndef _MYADDLIB_H_
#define _MYADDLIB_H_
#ifdef __cplusplus
extern "C" {
#endif
int add(int ,int );
#ifdef __cplusplus
}
#endif
#endif
/*************************************************************
FileName : myAddLib.c
FileFunc : 定义静态库实现文件
Version : V0.1
Author : Sunrier
Date : 2012-04-28
Descp : Linux下实现静态库
*************************************************************/
#include <stdio.h>
int add(int iArg1,int iArg2)
{
printf("iArg1= %d , iAgr2= %d \n",iArg1,iArg2);
return (iArg1+iArg2);
}
2.制作静态库文件
1.编译并生成目标文件:gcc -c myAddLib.c -o myAddLib.o
2.生成归档文件:ar -rc libmyAddlib.a myAddLib.o
gcc -o myAddLib.o -c myAddLib.c
把源程序文件编译成*.o。-c :只编译并生成目标文件。
ar -rc libmyAddLib.a myAddLib.o
执行完后会生成一个libmyAddLib.a 文件
-c create的意思
-r replace的意思,表示当插入的模块名已经在库中存在,则替换同名的模块.如果若干模块中有一个模块在库中不存在,ar显示一个错误消息,并不替换其他同名模块.默认的情况下,新的成员增加在库的结尾处,可以使用其他任选项来改变增加的位置。
3.使用静态库文件
/*************************************************************
FileName : testAddLib.c
FileFunc : 测试静态库文件
Version : V0.1
Author : Sunrier
Date : 2012-04-28
Descp : Linux下实现静态库
*************************************************************/
#include <stdio.h>
#include "myAddLib.h"
int main(int argc,char *argv[])
{
int iNumber1,iNumber2,iSum = 0;
iNumber1 = 10;
iNumber2 = 20;
iSum = add(iNumber1,iNumber2);
printf("iSum=%d\n".iSum);
printf("Hello Sunrier!\n");
return 0;
}
编译:gcc -o testAddLib testAddLib.c -L. -lmyAddLib
-L指定库路径,-lmyAddLib指定库的名称,不包含lib.a两部分。
第二节:动态函数库
动态函数库又叫共享库(share lib)。它是在运行时,由系统的动态函
数库"lib/ld.so"负责动态加载。
动态函数库也有自己的命名规则:lib+动态库名称+.so+.+版本号;比如libC++.so.6。
动态函数库有两种使用方式:动态链接(隐式调用)和动态调用(显式调用,利用lib/ld.so提供的函数,利用函数指针,动态调用一个类库中包含的函数,不需要知道动态库的头文件)。
隐式调用和显示调用:
隐式调用使用方便简单, 但其和静态库相似, 在编译期就和程序绑定了,灵活性差, 并且其生存期和进程一样, 进程开始, 调用开始, 进程结束, 动态库才卸载. 另外还需要将整个动态库全部加进内存.
显式调用使用起来比较复杂, 但是却可以在运行期间选择所需要调用的动态链接库, 并且可以控制动态库生存期, 需要加载时候再加载, 用完了就可以卸掉, 而且不用将整个动态库都放进内存, 只要加载要用到的函数即可.
隐式调用:
编写动态库文件
/*************************************************************
FileName : myAddLib.h
FileFunc : 定义头文件
Version : V0.1
Author : Sunrier
Date : 2012-05-02
Descp : Linux下实现动态库
*************************************************************/
#ifndef _MYADDLIB_H_
#define _MYADDLIB_H_
#ifdef __cplusplus
extern "C" {
#endif
int add(int ,int );
#ifdef __cplusplus
}
#endif
#endif
/*************************************************************
FileName : myAddLib.c
FileFunc : 定义动态库实现文件
Version : V0.1
Author : Sunrier
Date : 2012-05-02
Descp : Linux下实现动态库
*************************************************************/
#include <stdio.h>
int add(int iArg1,int iArg2)
{
printf("iArg1= %d , iAgr2= %d \n",iArg1,iArg2);
return (iArg1+iArg2);
}
制作库文件:
1.gcc -fipc myAddLib.c -o myAddLib.o
2.gcc -share -o libmyAddLib.so.1 myAddlib.o
或者 gcc -fipc -share -o libmyAddLib.so.1 myAddLib.c
其中-fipc用来产生和位置无关代码,-share用来生成动态共享库
隐式使用动态库文件
/*************************************************************
FileName : testAddLib.c
FileFunc : 测试动态库文件
Version : V0.1
Author : Sunrier
Date : 2012-05-02
Descp : Linux下实现动态库
*************************************************************/
#include <stdio.h>
#include "myAddLib.h"
int main(int argc,char *argv[])
{
int iNumber1,iNumber2,iSum = 0;
iNumber1 = 10;
iNumber2 = 20;
iSum = add(iNumber1,iNumber2);
printf("iSum=%d\n",iSum);
printf("Hello Sunrier!\n");
return 0;
}
编译目标文件
gcc -o testAddLib testAddLib.c -L. -lmyAddLib
-L指定动态函数库的位置供查找,注意L后面还有'.',表示动态函数库在本目录下查找。此时还不能立即./testAddLib,因为在动态函数库使用时,会查找/usr/lib或/lib目录下的动态函数库,而此时我们生成的库不在里边。
执行可执行文件
这个时候有好几种方法可以让他成功运行:
最直接最简单的方法就是把libmyAddLib.so放到/usr/lib或/lib中去或者修改LD_LIBRARY_PATH环境变量文件。
假设libmyAddLib.so在/home/Sunrier/lib
export LD_LIBRARY_PATH=/home/Sunrier/lib:$LD_LIBRARY_PATH
另外还可以在/etc/ld.so.conf文件里加入我们生成的库的目录,然后/sbin/ldconfig。
/etc/ld.so.conf是非常重要的一个配置文件,里面存放的是链接器和加载器搜索共享库时要检查的目录,默认是从/usr/lib或/lib中读取的,所以想要顺利运行,我们也可以把我们库的目录加入到这个文件中并执行/sbin/ldconfig。
动态调用:
根据前面提到的生成头文件libmyAddLib.so文件。
/*************************************************************
FileName : testAddLib.c
FileFunc : 测试动态库文件
Version : V0.1
Author : Sunrier
Date : 2012-05-03
Descp : Linux下实现动态库
*************************************************************/
#include <stdio.h>
#include <dlfcn.h>
#define SOFILE "./libmyAddLib.so"
int main(int argc,char *argv[])
{
int iNumber1,iNumber2,iSum = 0;
void *hdl = NULL;//动态库句柄
hdl = dlopen(SOFILE,RTLD_LAZY);//打开动态链接库
if( NULL==hdl )
{
printf("No libmyAddLib.so file\n");
return 1;
}
/*函数指针*/
int(*add)(int,int);
add = dlsym(hdl,"add");//查找符号表,定位共享函数
char *error = dlerror();//检测错误
if( error )
{
printf("No function!\n");
return 1;
}
iNumber1 = 10;
iNumber2 = 20;
iSum = add(iNumber1,iNumber2);//调用此共享函数
//iSum = (*add)(iNumber1,iNumber2);//调用此共享函数
printf("iSum=%d\n",iSum);
printf("Hello Sunrier!\n");
dlclose(hdl);//关闭共享库
hdl =NULL;
return 0;
}
dlfcn.h包含了动态调用函数库的相关的函数。
void *dlopen (const char *file, int arg );
//打开一个动态库的文件,arg代表打开的方式
/*
RTLD_LAZY
Relocations are performed at an implementation-dependent time.
RTLD_NOW
Relocations are performed when the object is loaded.
RTLD_GLOBAL
All symbols are available for relocation processing of other modules.
RTLD_LOCAL
All symbols are not made available for relocation processing by other modules.*/
void *dlsym(void *handle, const char *name);
void *handle;
int *iptr, (*fptr)(int);
/* open the needed object */
handle = dlopen("/usr/home/me/libfoo.so.1", RTLD_LAZY);
/* find the address of function and data objects */
fptr = (int (*)(int))dlsym(handle, "my_function");
iptr = (int *)dlsym(handle, "my_object");
/* invoke function, passing value of integer as a parameter */
(*fptr)(*iptr);
char *dlerror(void);
/*If successful, dlerror() returns a null-terminated character string. Otherwise, NULL is returned.*/
int dlclose(void *handle);
/*If the referenced object was successfully closed, dlclose() returns 0.
If the object could not be closed, or if handle does not refer to an open object,
dlclose() returns a non-zero value. More detailed diagnostic information will be available through dlerror().*/
编译和生成目标文件:
#gcc testAddLib.c -o testAddLib -ldl
其中-ldl用于加载dl库