在某些时候,您可能必须在运行时加载库才能使用其功能。 在为程序编写某种插件或模块体系结构时,这种情况最常见。
在Linux C/C++语言中,加载库非常简单,只需调用dlopen,dlsym和dlclose就足够了。
动态装载库API
动态库的运行时加载时通过一系列由动态链接器(dynamic linker)提供的API来实现的,这几个API的实现都是在libdl.so中的,且相关的声明和常量定义在头文件
下面列出了这些API:
void *dlopen(const char *filename, int flag);
/*
mode:分为这两种
RTLD_LAZY 暂缓决定,等有需要时再解出符号
RTLD_NOW 立即决定,返回前解除所有未决定的符号。
RTLD_LOCAL
RTLD_GLOBAL 允许导出符号
RTLD_GROUP
RTLD_WORLD
*/
void *dlsym(void *handle, const char *symbol);
int dlclose(void *handle);
char *dlerror(void);
gcc -fPIC、-shared选项
如果想创建一个动态链接库,可以使用 GCC 的-shared选项。输入文件可以是源文件、汇编文件或者目标文件。
另外还得结合-fPIC选项。-fPIC 选项作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code)。
这样一来,产生的代码中就没有绝对地址了,全部使用相对地址,所以代码可以被加载器加载到内存的任意位置,都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的
例如,从源文件生成动态链接库:
gcc -fPIC -shared libplugin.c -o libplugin.so
实例
使用 gcc 将 libshared.c 、libplugin.c 各编译成共享对象文件libshared.so、libplugin.so
Makefile
executable: main.c libshared.so libplugin.so
$(CC) -pie -fPIC -L. main.c -lshared -ldl -o executable
libshared.so: libshared.c
$(CC) -fPIC -shared -rdynamic libshared.c -o libshared.so
libplugin.so: libplugin.c
$(CC) -fPIC -shared libplugin.c -o libplugin.so
clean:
rm -f executable libshared.so libplugin.so testcase.zip
run: executable
./run.sh
zip: executable
rm -f testcase.zip
zip testcase.zip executable libshared.so libplugin.so run.sh
.PHONY: run clean zip
libplugin.c它有一个简单的函数调用extern函数libshared_get_value()。它不针对链接libshared.so
//libplugin.c
#include
extern int libshared_get_value();
void plugin_function() {
printf("plugin: value = %d\n", libshared_get_value());
}
libshared.c其中包含功能libshared_get_value()。它是作为名为的共享库构建的libshared.so。
int libshared_get_value()
{
return 42;
}
使用sh run.sh运行可执行文件。
LD_LIBRARY_PATH=. ./executable
main.c与libshared.so和链接libshared_get_value()。
#include
#include
typedef void (*plugin_function_t)();
extern int libshared_get_value();
int main(int argc, char *argv[])
{
void* plugin_handle = dlopen("./libplugin.so", RTLD_LAZY);
if (plugin_handle == NULL) {
printf("dlopen failed: %s\n", dlerror());
return 1;
}
plugin_function_t plugin_function = dlsym(plugin_handle, "plugin_function");
if (plugin_function == NULL) {
printf("plugin_function not found\n");
return 1;
}
printf("main: value = %d\n", libshared_get_value());
plugin_function();
return 0;
}
我们使用dlopen打开一个.so,并将其加载到进程的地址空间,完成初始化过程。当库被装入后,可以把 dlopen() 返回的句柄作为给 dlsym() 的第一个参数,以获得符号在库中的地址。使用这个地址,就可以获得库中特定函数的指针,并且调用装载库中的相应函数。
总结
上面我们使用dlopen、dlsym、dlclose加载动态链接库,为了使程序方便扩展,具备通用性,可以采用插件形式,这种情况在开发中最常见。
而Linux 下动态链接库的文件后缀为.so,它是一种特殊的目标文件,可以在程序运行时被加载进来。
使用动态链接库的优点是:程序的可执行文件更小,便于程序的模块化以及更新,同时,有效内存的使用效率更高。
欢迎关注微信公众号【程序猿编码】,添加本人微信号(17865354792),回复:领取学习资料。或者回复:进入技术交流群。网盘资料有如下: