加载动态链接库的相关接口dlopen,dlsym和dlclose等

nginx从1.9.11开始,支持运行时动态加载模块了,浏览了下代码实现,在类unix系统上是用dlopen,dlsym和dlclose实现的。下面看看这几个函数的功能。  


dlopen

功能:

打开一个动态链接库

包含头文件:

#include <dlfcn.h>

函数定义:

void * dlopen(const char * pathname, int mode); 

函数描述:

dlopen()以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程。使用dlclose()来卸载打开的库。 

mode的取值:

RTLD_LAZY 暂缓决定,等有需要时再解出符号。

RTLD_NOW 立即决定,返回前解除所有未决定的符号。 

RTLD_LOCAL

RTLD_GLOBAL 允许导出符号。

RTLD_GROUP

RTLD_WORLD

返回值:

打开错误返回NULL

成功,返回库引用

编译时候要加入 -ldl (指定dl


dlsym

功能:

根据动态链接库操作句柄与符号,返回符号对应的地址。

包含头文件:

#include <dlfcn.h>

函数定义:

void*dlsym(void* handle,const char* symbol);

函数描述:

dlsym根据动态链接库操作句柄handle与符号symbol,返回符号对应的地址。使用这个函数不但可以获取函数地址,也可以获取变量地址。

handle是由dlopen打开动态链接库后返回的指针,symbol就是要求获取的函数或全局变量的名称。


dlclose

功能:

关闭指定句柄的动态链接库。

包含头文件:

#include <dlfcn.h>

函数定义:int dlclose(void* handle);

函数描述:

关闭由dlopen打开的动态链接库句柄,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。

生成动态库:

hello.c:

#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

typedef struct {
    const char *module;
    int (*getvalue)(char *val);
    int (*printfhello)(void);
} hello_dl_api;

int getvalue(char *val)
{
    int retval = -1;

    if (val) {
        retval = sprintf(val, "remain smile");
	printf("%s, %d, val = %s\n", __FUNCTION__, __LINE__, val);
    }

    return retval;
}

int printfhello(void)
{
    int retval = -1;

    printf("%s, %d, hello world\n", __FUNCTION__, __LINE__);
    return 0;
}

const hello_dl_api hello = {
    .module = "hello",
    getvalue,
    printfhello
};
编译成动态库:gcc -shared -o hello.so hello.c

上面由一个全局的结构体hello指向两个函数。在dlsym的描述可知,返回的句柄不仅可以获取函数的地址,还可以获取全局变量的地址。

测试文件:

hello_dlopen.c

#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>

typedef struct {
    const char *module;
    int (*getvalue)(char *val);
    int (*printfhello)(void);
} hello_dl_api;

int main(int argc, char **argv)
{
    hello_dl_api *hello;
    int i = 0;
    void *handle;
    char value[20] = {0};

    handle = dlopen("hello.so", RTLD_LAZY);
    if (!handle) {
        printf("%s, %d, handle == NULL\n", __FUNCTION__, __LINE__);
        return -1;
    }

    dlerror();

    hello = dlsym(handle, "hello");
    if (!hello) {
        printf("%s, %d, handle == NULL\n", __FUNCTION__, __LINE__);
        return -1;
    }

    if (hello && hello->printfhello)
        i = hello->printfhello();

    printf("%s, %d, i = %d\n", __FUNCTION__, __LINE__, i);
    if (hello && hello->getvalue)
        i = hello->getvalue(value);

        if (hello && hello->module)
            printf("%s, %d, module = %s\n", __FUNCTION__, __LINE__, hello->module);

    dlclose(handle);
    return 0;
}
编译指令:gcc -o hello_test hello_dlopen.c -ldl

执行结果:

./test

printfhello, 28, hello world

main, 37, i = 0

getvalue, 18, val = remain smile

main, 42, module = hello 

由此可见,dlsym找到全局结构体hello后,可以通过这个全局结构体指针来调用库里面的函数。

本文参考:

http://blog.csdn.net/jernymy/article/details/6903683

http://blog.csdn.net/bailyzheng/article/details/17613847

猜你喜欢

转载自blog.csdn.net/winshining/article/details/62045052
今日推荐