Linux编程中链接库的使用
程序员文章站
2022-03-28 12:47:54
链接库本质上是一段可执行的二进制代码,可以被操作系统载入内存执行。按加载的时机不同,链接库可以分为静态链接库和动态链接库。 静态链接库:编译过程中加载进可执行文件的库(静态库省去了运行时加载的消耗,但会导致可执行文件体积增大)动态链接库:程序运行过程中,动态加载进内存的库(动态库加载需要资源消耗,但 ......
链接库本质上是一段可执行的二进制代码,可以被操作系统载入内存执行。按加载的时机不同,链接库可以分为静态链接库和动态链接库。
静态链接库:编译过程中加载进可执行文件的库(静态库省去了运行时加载的消耗,但会导致可执行文件体积增大)
动态链接库:程序运行过程中,动态加载进内存的库(动态库加载需要资源消耗,但可以显著降低可执行文件体积)
动态链接库:程序运行过程中,动态加载进内存的库(动态库加载需要资源消耗,但可以显著降低可执行文件体积)
什么情况下使用链接库?
1、大型软件项目中,不同模块可以各自完成,然后封装成链接库供上层模块调用。
2、一些通用的功能,如文件处理、数据库接口、算法等,可以封装成库,从而避免每个模块都维护一份独立的通用代码。
3、项目中有很多小的业务模块,可以把各模块制作成统一接口的动态库,主进程根据实际业务按需加载。
1、大型软件项目中,不同模块可以各自完成,然后封装成链接库供上层模块调用。
2、一些通用的功能,如文件处理、数据库接口、算法等,可以封装成库,从而避免每个模块都维护一份独立的通用代码。
3、项目中有很多小的业务模块,可以把各模块制作成统一接口的动态库,主进程根据实际业务按需加载。
静态库的使用,主要有两点:
一是在主程序里需有静态库接口函数的声明,一般使用一个头文件;
二是在编译时加载静态库,如linux gcc编译时可以用形如-lcpplib的选项来加载一个文件名为libcpplib.a或libcpplib.so的库。
这里说明下命名规范,一般建议静态库的后缀用.a,动态库后缀用.so。
满足上面两点,静态库里的函数就可以像平常一样直接使用了。
一是在主程序里需有静态库接口函数的声明,一般使用一个头文件;
二是在编译时加载静态库,如linux gcc编译时可以用形如-lcpplib的选项来加载一个文件名为libcpplib.a或libcpplib.so的库。
这里说明下命名规范,一般建议静态库的后缀用.a,动态库后缀用.so。
满足上面两点,静态库里的函数就可以像平常一样直接使用了。
动态库的使用,相对要复杂一些。
这里抛个问题,既然是动态加载的,主程序怎么知道里面有什么函数,怎么调用呢?
在linux系统里,可以nm查看链接库的符号表,也即是里面的函数表。linux程序的动态库调用,也提供了4个加载动态库相关的函数:dlopen、dlsym、dlerror和dlcolose。
#include <dlfcn.h>/*包含的头文件*/
/*pathname为动态库文件名;mode是打开方式,如rtld_now表示直接解析出动态库中所有的符号*/
/*此函数返回一个void指针指向加载的库的句柄*/
void * dlopen( const char * pathname, int mode);
/*handle为dlopen返回的句柄,symbol为函数名*/
/*此函数返回库里指定名称的函数的指针*/
void*dlsym(void*handle,constchar*symbol);
/*此函数在加载链接库出错时,返回错误信息*/
char *dlerror(void);
/*关闭指定的动态库句柄*/
int dlclose (void *handle);
这里抛个问题,既然是动态加载的,主程序怎么知道里面有什么函数,怎么调用呢?
在linux系统里,可以nm查看链接库的符号表,也即是里面的函数表。linux程序的动态库调用,也提供了4个加载动态库相关的函数:dlopen、dlsym、dlerror和dlcolose。
#include <dlfcn.h>/*包含的头文件*/
/*pathname为动态库文件名;mode是打开方式,如rtld_now表示直接解析出动态库中所有的符号*/
/*此函数返回一个void指针指向加载的库的句柄*/
void * dlopen( const char * pathname, int mode);
/*handle为dlopen返回的句柄,symbol为函数名*/
/*此函数返回库里指定名称的函数的指针*/
void*dlsym(void*handle,constchar*symbol);
/*此函数在加载链接库出错时,返回错误信息*/
char *dlerror(void);
/*关闭指定的动态库句柄*/
int dlclose (void *handle);
下面是演示用的代码:
该段代码实现一个动态库调用接口dllrun,通过向该接口传递业务数据、库名、函数名,可以指定相应的库函数来处理数据。
同时iflag控制该动态库在使用完后是否立即释放,如果不释放(频繁使用的库不释放可以节省加载的开支),则将库句柄指针压入map。
#include <dlfcn.h>
该段代码实现一个动态库调用接口dllrun,通过向该接口传递业务数据、库名、函数名,可以指定相应的库函数来处理数据。
同时iflag控制该动态库在使用完后是否立即释放,如果不释放(频繁使用的库不释放可以节省加载的开支),则将库句柄指针压入map。
#include <dlfcn.h>
typedef int (*dll_func)(char*);/*动态库的函数声明形式*/
map<const char*, dll_func> mapfunc;/*保存不释放的动态库*/
map<const char*, dll_func> mapfunc;/*保存不释放的动态库*/
/*
函数功能:加载动态库并执行相应的业务函数
输入参数:strdata - 业务数据,根据实现业务情况选择用什么方式传递业务数据,比如数据多且可以按字段划分,可以用map
strdllname - 需要加载的库名称
strfunc - 业务的函数名称
iflag - 加载方式:
0 - 第1次加载后handle保存到map,之后调用不重新加载
1 - 每次加载均释放掉,下次重新加载
输出参数:strdata - 函数执行后输出数据
返回值:0 - 成功;其他失败
*/
int dllrun(char* strdata, const char* strdllname, const char* strfunc, int iflag)
{
int iret=-1;
dll_func pfunc=null;/*业务函数的指针*/
void* phandle=null;
if(null == strfunc)
{
iret = -1;
goto dllrun_return;
}
if(!iflag)
{
phandle = mapfunc[strdllname];
}
if(null == phandle)
{ /*加载动态库*/
phandle = dlopen(strdllname, rtld_now);
if(null == phandle)
{
printf("加载链接库[%s]失败: %s\n", strdllname, dlerror());
iret = -2;
goto dllrun_return;
}
}
func = dlsym(phandle, strfunc);/*获取业务函数指针*/
if(null == func)
{
printf("获取动态库[%s]的函数[%s]的指针失败: %s\n", strdllname, strfunc, dlerror());
iret = -3;
goto dllrun_return;
}
iret = func(strdata);/*业务执行*/
if(0 != iret)
{
goto dllrun_return;
}
函数功能:加载动态库并执行相应的业务函数
输入参数:strdata - 业务数据,根据实现业务情况选择用什么方式传递业务数据,比如数据多且可以按字段划分,可以用map
strdllname - 需要加载的库名称
strfunc - 业务的函数名称
iflag - 加载方式:
0 - 第1次加载后handle保存到map,之后调用不重新加载
1 - 每次加载均释放掉,下次重新加载
输出参数:strdata - 函数执行后输出数据
返回值:0 - 成功;其他失败
*/
int dllrun(char* strdata, const char* strdllname, const char* strfunc, int iflag)
{
int iret=-1;
dll_func pfunc=null;/*业务函数的指针*/
void* phandle=null;
if(null == strfunc)
{
iret = -1;
goto dllrun_return;
}
if(!iflag)
{
phandle = mapfunc[strdllname];
}
if(null == phandle)
{ /*加载动态库*/
phandle = dlopen(strdllname, rtld_now);
if(null == phandle)
{
printf("加载链接库[%s]失败: %s\n", strdllname, dlerror());
iret = -2;
goto dllrun_return;
}
}
func = dlsym(phandle, strfunc);/*获取业务函数指针*/
if(null == func)
{
printf("获取动态库[%s]的函数[%s]的指针失败: %s\n", strdllname, strfunc, dlerror());
iret = -3;
goto dllrun_return;
}
iret = func(strdata);/*业务执行*/
if(0 != iret)
{
goto dllrun_return;
}
dllrun_return:
if(iflag)
{
if(null != phandle)
{
dlclose(phandle);
}
}
else
{
mapfunc[strdllname] = phandle;
}
return iret;
}
if(iflag)
{
if(null != phandle)
{
dlclose(phandle);
}
}
else
{
mapfunc[strdllname] = phandle;
}
return iret;
}
上一篇: 火娃
下一篇: 艘载有多国乘客的船要沉了