3,运行时动态加载.so
  动态库的运行时加载时通过一系列由动态链接器(dynamic linker)提供的API来实现的,这几个API的实现都是在libdl.so中的,且相关的声明和常量定义在头文件<dlfcn.h>中。
  dlopen():主要用来打开一个.so,并将其加载到进程的地址空间,完成初始化过程,原型如下:
  void *dlopen(const char *filename, int flag);
  filename:the dynamic library file named by the null-terminated string;对于路径设置为相对路径或路径情形下,dlopen()是如何查找该动态库文件的,这个直接参考man;
  flag:用于表示函数符号的解析方式,如:RTLD_LAZY表示使用延迟绑定,只有在函数第一次被使用时才会去绑定,即PLT机制;相对应的为RTLD_NOW表示当模块被加载时即完成所有的函数绑定工作,如果有任何未定义的符号引用的绑定工作无法完成,dlopen都直接返回错误;其他的flag标识可以直接参考man;
  返回值是被加载的模块的句柄,该句柄会被其他的几个API使用到,如果加载失败,则返回NULL。
  dlsym():通过此函数来找到所需要的符号,定义如下:
  void *dlsym(void *handle, const char *symbol);
  第一个参数handle是dlopen返回的句柄,第二个参数symbol表示要查找的符号的名字,一个以‘’ 结尾的字符串;
  如果dlsym找到了相应的符号,则返回该符号的值;如果没有找到,则返回NULL。
  对于不同类型的符号,返回的指针意义不同:如果为函数,则为函数地址;如果为变量,则为变量的地址;如果为常量,则应该为常量的值;
  dlerror():在调用dlopen,dlsym,dlclose之后,都可以通过dlerror()来判断上一次调用是否成功,dlerror的返回值为char*,如果返回NULL,则表示调用成功;否则返回对应的错误消息;函数原型声明如下:
  char *dlerror(void);
  dlclose():卸载一个已经加载的模块。
  在系统内部会维持一个加载引用计数器,每次调用dlopen()来加载模块时,相应的计数器加1;每次调用dlclose()卸载某模块时,相应计数器减1。当计数器等于0时,该模块才会真正被卸载掉。
  下面直接上示例:

 

// ex_dload_so.cpp
#include <iostream>
using namespace std;
#include <dlfcn.h>
int main()
{
// 为上面生成的.so文件的路径
const char *MYLIB_PATH = "/home/steven/test/c++/mylib.so";
// 加载指定的 .so 文件
void *handle = dlopen(MYLIB_PATH, RTLD_NOW);
if (handle == NULL)
{
cerr << "Open library " << MYLIB_PATH << " error: " << dlerror() << endl;
return -1;
}
// 查找函数foobar,并返回函数指针
void (*fn_foobar)(int);
fn_foobar = (void (*)(int))dlsym(handle, "foobar");
char *error = NULL;
if ((error = dlerror()) != NULL)
{
cerr << "Symbol foobar not found: " << error << endl;
dlclose(handle);
return -2;
}
// 调用对应的foobar函数打印输出
fn_foobar(123);
dlclose(handle);
return 0;
}

  编译运行效果:
  [steven@sasd c++]$ g++-ldl -o ex_dload_so ex_dload_so.cpp
  [steven@sasd c++]$ ./ex_dload_so
  Printing from mylib.so 123
  [steven@sasdc++]$