函数库:函数库是实现了某一类功能的若干的函数的数据和二进制机器代码的集合。
库中虽然是函数的二进制机器代码集合,但不能独立执行,可被载入内存和其它程序结合起来执行。
函数库分为静态库和动态库两种,函数库均由目标文件(即 .o文件)生成。
【一】静态库
1,静态库文件名的命名
以lib为前缀,紧接是静态库名,扩展名为.a 例:libmytest.a
静态库文件名: libmytest.a
静态库名: mytest
2,静态库的生成
a.由C源文件生成目标文件(.o)
$g++ -c test.cpp -o test.o
b.用ar命令将若干目标文件生成静态库
$ar crs libmytest.a test1.o test2.o
其中
c:表示创建一个库
r:表示替换方式在库中插入模块
s:将目标文件索引加入库中
g++ -o a.out test.cpp -L. -lmytest
g++ test.cpp -L. -lmytest -o a.out -I.
-L 指定库所在路径
-l 指定链接的库名
-I 指定头文件所在路径
注意:-l后跟的是库名而非库文件名
【二】动态库(也称共享库)
动态库在链接时由链接器链接,可执行程序运行时由加载器载入内存
加载器默认寻找动态库的路径 /lib /usr/lib
1,动态库的命名以lib为前缀,紧接是动态库名,扩展名为.so 例:libcalc.so
动态库文件名 :libmytest.so
动态库名: mytest
2,生成动态库
a,先得到目标文件
$g++ -c test1.cpp test2.cpp
b,生成与位置无关的动态库
$g++ -shared -fPIC test1.o test2.o -o libmytest.so
3,使用动态库
a.在链接时使用
$g++ test.cpp -L. -lmytest -I. -o a.out
4,运行时链接动态库,出现的问题
$./shared_lib/test: error while loading shared libraries: libmytest.so: cannot open shared object file: No such file or directory
可以查看程序执行时调用动态库的过程:
$ ldd a.out
【如何让加载器找到自己的共享库】
The necessary shared libraries needed by the program are searched for in the following order
o Using the environment variable LD_LIBRARY_PATH (LD_AOUT_LIBRARY_PATH for a.out programs). Except if the
executable is a setuid/setgid binary, in which case it is ignored.
o From the cache file /etc/ld.so.cache which contains a compiled list of candidate libraries previously
found in the augmented library path. Libraries installed in hardware capabilities directories (see
below) are prefered to other libraries.
o In the default path /lib, and then /usr/lib.
1.使用环境变量LD_LIBRARY_PATH
a.将自己动态库所在路径加入LD_LIBRARY_PATH
$LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/will/IO/Nov19/lib/shared_lib/
b.导出该变量
export LD_LIBRARY_PATH
2.在/etc/ld.so.cache文件中追加自己的动态库所在路径
a.在/etc/ld.so.conf.d/目录下以root权限新建一个.conf后缀的文件 ,其内容写入动态库路径写入
b.以root权限执行ldconfig
3.将自己的动态库拷入/lib 或/usr/lib
注意:如果这样,链接时可不指定库所在路径,仍需要指定库名,因为gcc不能自动识别第三方库
【三】编译参数解析
GCC/G++命令行的一个选项:
-shared : 该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件
-fPIC :表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。
-L. :表示要连接的库在当前目录中
-ltest :编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称
LD_LIBRARY_PATH:这个环境变量指示动态连接器可以装载动态库的路径。
当然如果有root权限的话,可以修改/etc/ld.so.conf文件,然后调用 /sbin/ldconfig来达到同样的目的,不过如果没有root权限,那么只能采用输出LD_LIBRARY_PATH的方法了。
调用动态库的时候有几个问题会经常碰到,有时,明明已经将库的头文件所在目录 通过 “-I” include进来了,库所在文件通过 “-L”参数引导,并指定了“-l”的库名,但通过ldd命令察看时,就是死活找不到你指定链接的so文件,这时你要作的就是通过修改 LD_LIBRARY_PATH或者/etc/ld.so.conf文件来指定动态库的目录。通常这样做就可以解决库无法链接的问题了
【四】静态库和动态库的区别
共同点:
.a和.so都是.o目标文件的集合,换句话说,都是由目标文件生成。这些目标文件中含有一些函数的定义(机器码),而这些函数将在连接时会被最终的可执行文件用到。
不同点:
静态库.a :当程序与静态库连接时,库中目标文件所含的所有将被程序使用的函数的机器码被copy到最终的可执行文件中. 生成的可执行程序较大,不过程序运行时不再需要静态库。
静态库只在链接时使用一次,程序运行时不依赖静态库,程序移植方便;
静态库有个缺点:占用磁盘和内存空间. 静态库会被添加到和它连接的每个程序中, 而且这些程序运行时, 都会被加载到内存中. 无形中又多消耗了更多的内存空间.
共享库不仅在链接时需要使用,在程序运行时依然需要使用,移植的程序正常运行前提是库已经正确移植
当静态库和动态库同名时, gcc命令将优先使用动态库
静态库链接时搜索路径顺序:
1. ld会去找GCC命令中的参数-L
2. 再找gcc的环境变量LIBRARY_PATH
3. 再找内定目录 /lib /usr/lib /usr/local/lib 这是当初compile gcc时写在程序内的
动态链接时、执行时搜索路径顺序:
1. 编译目标代码时指定的动态库搜索路径;
2. 环境变量LD_LIBRARY_PATH指定的动态库搜索路径;
3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径;
4. 默认的动态库搜索路径/lib;
5. 默认的动态库搜索路径/usr/lib。
LIBRARY_PATH环境变量:指定程序静态链接库文件搜索路径
LD_LIBRARY_PATH环境变量:指定程序动态链接库文件搜索路径
【五】动态链接函数库
涉及到的库<dlfcn.h>,该库中提供了四个轻松调用动态链接库的API
注意:编译时,需要链接库 -ldl
1,进行动态链接库的相关函数
void *dlopen (const char *pathname, int open_mode)
功能:dlopen函数是打开动态链接库文件的API
参数 pathname: 是so文件的路径,
参数 open_mode: 是打开so文件的模式,
打开模式常用的有两种:RTLD_NOW和RTLD_LAZY。
RTLD_NOW:在dlopen()方法调用完成之前就去动态的解析so文件里面的所有未定义的符号,若无法解析,则打开失败。
RTLD_LAZY: 只有当so文件里面的未定义的符号在真正使用的时候才去解析。
【注意】:如果加载的动态库还依赖其他的动态库,必须使用RTLD_NOW。只能打开动态库。
返回值: 函数调用成功,则返回该so文件的句柄(指针)so_handle,否则返回NULL。
void *dlsym (void * handle,const char* symbol)
功能:dlsym根据动态链接库操作句柄(handle)与符号(symbol),返回符号对应的地址。(注意:返回值是地址)
使用这个函数不但可以获取函数的地址,也可以获取变量(全局变量)的地址。
参数 handle:使用dlopen()打开动态链接库后返回的指针(so句柄)
参数 symbol:要求获取的函数或全局变量的名称。
【注意】:该so方法中不能有重载的函数,当然,c语言是不支持函数重载的
返回值:函数调用成功则返回symbol函数的地址或者全局变量的地址,否则返回NULL。
int dlclose (void *so_handle)
功能: 关闭dlopen()返回的so句柄
【注意】:如果在使用dlsym()返回的函数指针的时候调用了该方法,那么,
肯定会出现Segment fault的错误,因为调用此方法之后,代表对so动态库的资源回收。
char *dlerror (void)
功能:返回调用上述方法失败时的具体错误信息。
2,用法示例
test.cpp (该文件用于生成动态库so)
extern "C" {
int max_priority = 100 ;
const char* getVersion(){
static char* version = "123";
return version ;
}
const char* getPriority(){
static char* priority = "1";
return priority ;
}
}
下面是测试文件:main.cpp
#include <iostream>
#include <string>
#include <dlfcn.h>
int main() {
std::string version ;
std::string priority ;
void * handle = NULL ;
handle = diopen("/home/test/libtest.so",RTLD_NOW);
if(handle == NULL) {
std::cout <<dlerror()<<std::endl;
return -1 ;
}
const char*(*_pgetversion)(void); //定义 一个函数指针
//获取链接库中 getVersion函数的地址
_pgetversion = (const char*(*)(void))dlsym(handl,"getVersion");
if(_pgetversion == NULL){
std::cout <<dlerror()<<std::endl;
return -1;
}
version = (*_pgetversion)();
const char*(*_pgetpriority)(void);
_pgetpriority = (const char*(*)(void))dlsym(handl,"getPriority");
if(_pgetpriority == NULL){
std::cout <<dlerror()<<std::endl;
return -1;
}
priority = (*_pgetpriority)();
int * _pmaxpriority ; //定义一个int型的指针
//获取动态链接库中 全局变量max_priority的地址
_pmaxpriority = (int *)dlsym(handle,"max_priority");
if(_pmaxpriority == NULL){
std::cout <<dlerror()<<std::endl;
return -1 ;
}
int ret = *_pmaxpriority ;
std::cout<<"version: "<<version<<std::endl;
std::cout<<"priority: "<<priority<<std::endl;
std::cout<<"max_priority: "<<ret<<std::endl;
dlclose(handle); //关闭动态库
return 0 ;
}
运行的结果是:
version:123
priority:1
max_priority:100