0%

动态库的显式调用

动态链接库在使用时,分为“隐式调用”和“显式调用”两种:

1.如果是隐式调用,则与静态库的使用方法差不多,注意需要包含导出函数的头文件,即mylib.h:

#include ...
#include "mylib.h"

int main()
{
    Print();
}

编译方法:

gcc -o main main.c -L./ mylib.so

注意要加上动态链接库的搜索路径,否则编译器只会到系统路径中去寻找。

2.显式调用的方式,不必包含mylib.h,但是需要增加几个系统调用:

#include ...
#include  <dlfcn.h>// 显式加载需要用到的头文件

int main()
{
    void *pdlHandle = dlopen("./mylib.so", RTLD_LAZY); // RTLD_LAZY 延迟加载
    char *pszErr = dlerror();
    if( !pdlHandle || pszErr )
    {
        printf("Load mylib failed!n")
            return 1;
    }

    void (*Print)() = dlsym(pdlHandle, "Print"); // 定位动态链接库中的函数
    if( !Print )
    {
        pszErr = dlerror();
        printf("Find symbol failed!%sn", pszErr);
        dlclose(pdlHandle);
        return 1;
    }

    Print(); // 调用动态链接库中的函数

    dlclose(pdlHandle); // 系统动态链接库引用数减1

    return 0;
}

可以看到,显式调用的代码看上去要复杂很多,但是却比隐式调用要灵活,
不必在编译时就确定要加载哪个动态链接库,可以在运行时再确定,甚至重新加载。

看一下显式调用的编译方式:

gcc -ldl -o main main.c

注意要添加-ldl选项,以使用显式调用相关的函数调用

手动加载动态链接库dlopen dlsym dlcolose

打开动态链接库

#include <dlfcn.h>
void *dlopen(const char *filename, int flag);

该函数返回操作句柄,如:

void *pHandle = dlopen(strSoFilePath, RTLD_LAZY);

取动态对象地址

#include <dlfcn.h>
void *dlsym(void *pHandle, char *symbol);

dlsym根据动态链接库操作句柄(pHandle)与符号(symbol),返回符号对应的地址。
使用这个函数不但可以获取函数地址,也可以获取变量地址。比如,假设在so中
定义了一个void mytest()函数,那在使用so时先声明一个函数指针:
void (*pMytest)(),然后使用dlsym函数将函数指针pMytest指向mytest函数,
pMytest = (void (*)())dlsym(pHandle, "mytest");

关闭动态链接库

#include <dlfcn.h>
int dlclose(void *handle);

该函数将该.so的引用计数减一,当引用计数为0时,将它从系统中卸载。

动态库错误函数

#include <dlfcn.h>
const char *dlerror(void);

当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时 表示没有错误信息。

在取到函数执行地址后,就可以在动态库的使用程序里根据动态库提供的函数接口调用动态库里的函数。
在编写调用动态库的程序的Makefile文件时,需要加入编译选-ldl。

void *dlsym(void *handle, char *symbol) 的参数可以看出,
该函数只传两个参数:一个指向so的handle和一个函数的symbol,
所以so里面的函数应该不允许重载,否则根据一个symbol不能确定指向那个函数。