C/C++:使用dlopen、dlsym、dlclose运行时装载动态库

本文介绍了如何使用dlopen、dlsym、dlclose在C/C++中动态加载和使用动态链接库。通过实例展示了从加载动态库到获取并调用库中函数的全过程。

C/C++:使用dlopen、dlsym、dlclose运行时装载动态库

一个可执行程序可能与多个动态库有关联,通常是在程序运行时将必要的动态库装载入进程实体(内存中);

另外一种则是使用dlopen/dlsym/dlclose来动态地将动态库装载到当前进程实体中.

示例:

动态库:

count.h

#ifndef _COUNT_H
#define _COUNT_H
int count;

int get();
void inc();

#endif

count.c

#include "count.h"

int get()
{
    return count;
}

void inc()
{
    count++;
}

编译动态库:

[test1280@localhost ~]$ gcc -fPIC -c count.c
[test1280@localhost ~]$ gcc -shared count.o
[test1280@localhost ~]$ ll
total 20
-rw-rw-r--. 1 test1280 test1280   76 Oct 21 04:43 count.c
-rw-rw-r--. 1 test1280 test1280   77 Oct 21 04:42 count.h
-rw-rw-r--. 1 test1280 test1280 1568 Oct 21 04:56 count.o
-rwxrwxr-x. 1 test1280 test1280 7913 Oct 21 04:57 libcount.so

这一小节是生成动态库,之前有介绍,不再赘述.

dlopen/dlsym/dlclose使用示例:

#include <stdio.h>
#include <dlfcn.h>

#define NUM 6
#define LIBPATH "/home/test1280/libcount.so"

int main()
{
    void *handler = dlopen(LIBPATH, RTLD_LAZY);
    if (handler == NULL)
    {
        printf("ERROR:%s:dlopen\n", dlerror());
        return -1;
    }

    void (*inc)() = (void (*)())dlsym(handler, "inc");
    if (inc == NULL)
    {
        printf("ERROR:%s:dlsym\n", dlerror());
        return -1;
    }

    int i = 0;
    for (; i < NUM; i++)
        inc();

    int (*get)() = (int (*)())dlsym(handler, "get");
    if (get == NULL)
    {
        printf("ERROR:%s:dlsym\n", dlerror());
        return -1;
    }
    printf("INFO:get() return %d\n", get());

    dlclose(handler);

    return 0;
}

编译:

[test1280@localhost ~]$ gcc -o main main.c -ldl

注意:

源文件中没有包含count.h头文件;

编译时没有使用-l指定libcount.so;

[test1280@localhost ~]$ ll
total 36
-rw-rw-r--. 1 test1280 test1280   76 Oct 21 04:43 count.c
-rw-rw-r--. 1 test1280 test1280   77 Oct 21 04:42 count.h
-rw-rw-r--. 1 test1280 test1280 1568 Oct 21 04:56 count.o
-rwxrwxr-x. 1 test1280 test1280 7913 Oct 21 04:57 libcount.so
-rwxrwxr-x. 1 test1280 test1280 8761 Oct 21 05:12 main
-rw-rw-r--. 1 test1280 test1280  620 Oct 21 05:11 main.c

运行:

[test1280@localhost ~]$ ./main
INFO:get() return 6

DESCRIPTION:

The four functions dlopen(), dlsym(), dlclose(), dlerror() implement the interface to the dynamic linking loader.
void *dlopen(const char *filename, int flag);

The function dlopen() loads the dynamic library file named by the null-terminated string filename and returns an opaque "handle" for the  dynamic  library.
void *dlsym(void *handle, const char *symbol);

The  function dlsym() takes a "handle" of a dynamic library returned by dlopen() and the null-terminated symbol name, returning the address where that symbol is loaded into memory.
int dlclose(void *handle);

The function dlclose() decrements the reference count on the dynamic library handle handle.
If the reference count drops to zero and no other loaded  libraries use symbols in it, then the dynamic library is unloaded.
char *dlerror(void);

The function dlerror() returns a human readable string describing the most recent error that occurred from dlopen(), dlsym() or dlclose() since the last call to dlerror().

简单点说,就是:

1.dlopen将指定的动态库以特定的方式装载到当前进程实体,并返回一个可操作的句柄,用以后续获取函数地址等操作;

2.dlsym从指定的(由dlopen的返回值指定)库中获得指定的函数(第二个参数为函数名);

3.dlclose可将关闭卸载动态库;注意,实际是减掉一个对动态库的引用(ref),仅当减到0时才会触发卸载动态库的操作;

4.dlerror返回一个字符串用以描述错误;

通常的三段式就是:先打开(dlopen),然后获得需要的函数(dlsym),然后调用函数,最后关闭(dlclose).

再多测试下dlerror:

给出一个不存在的动态库路径:
#define LIBPATH "/home/test1280/libcount1.so"

[test1280@localhost ~]$ ./main
ERROR:/home/test1280/libcount1.so: cannot open shared object file: No such file or directory:dlopen
给出不存在的函数名称:
void (*inc)() = (void (*)())dlsym(handler, "inc_test1280");

[test1280@localhost ~]$ ./main
ERROR:/home/test1280/libcount.so: undefined symbol: inc_test1280:dlsym

其余错误可以自行尝试.

实践是检验真理的唯一标准.

Just Do It!

C++中增加动态库主要有隐式调用和显式调用两种方法: ### 隐式调用 隐式调用是指在编译和链接指定动态库的相关信息,程序在启动会自动加载动态库。具体步骤如下: 1. **包含头文件**:在代码中包含动态库提供的头文件,以使用其中定义的函数和类。 ```cpp #include "library.h" ``` 2. **链接动态库**:在编译命令中指定要链接的动态库。例如,使用g++编译,可以使用`-l`选项指定名,使用`-L`选项指定文件所在的目录。 ```sh g++ main.cpp -o main -L/path/to/library -llibrary ``` 3. **运行加载**:程序在运行会自动从指定的目录加载动态库。需要确保动态库文件在系统的搜索路径中,或者通过设置`LD_LIBRARY_PATH`环境变量来指定文件的路径。 ```sh export LD_LIBRARY_PATH=/path/to/library:$LD_LIBRARY_PATH ./main ``` ### 显式调用 显式调用是指在程序运行使用系统提供的API来加载和卸载动态库。在C++中,显式加载动态库存在一定困难,部分原因是C++的name mangling,另一部分是没有合适的API来装载类。name mangling可以通过`extern "C"`解决,只有非成员函数才能被声明为`extern "C"`,并且不能被重载,但它仍然是完全的C++函数,可以使用任何C++特性和各种类型的参数[^1]。具体步骤如下: 1. **包含必要的头文件**:在代码中包含`<dlfcn.h>`头文件,该头文件提供了动态加载的相关函数。 ```cpp #include <dlfcn.h> ``` 2. **打开动态库**:使用`dlopen`函数打开动态库文件,并返回一个句柄。 ```cpp void* handle = dlopen("/path/to/library.so", RTLD_LAZY); if (!handle) { // 处理错误 } ``` 3. **获取函数指针**:使用`dlsym`函数根据函数名获取动态库中函数的指针。 ```cpp typedef void (*FunctionType)(); FunctionType func = (FunctionType)dlsym(handle, "function_name"); if (!func) { // 处理错误 } ``` 4. **调用函数**:通过函数指针调用动态库中的函数。 ```cpp func(); ``` 5. **关闭动态库**:使用`dlclose`函数关闭动态库,并释放相关资源。 ```cpp dlclose(handle); ```
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值