共享库的高级特性

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/vrg000/article/details/78139153

库的动态加载

一般用于实现插件功能,可通过调用函数来加载、卸载共享库。可以查找共享库中函数名、变量名所对应的指针。

# include <dlfcn.h>

void *dlopen(const char *libfilename, int flags);   //打开共享库
int dlclose(void *handle);                          //关闭共享库
const char *dlerror(void);                          //返回错误字符串
void *dlsym(void *handle, char *symbol);            //查找共享库中的符号

注意:使用共享库的动态加载,在gcc链接时,要加入 -ldl 选项。

dlopen() 打开共享库

void *dlopen(const char *libfilename, int flags);
  • void *返回值:成功返回共享库句柄,失败返回NULL
  • const char *libfilename:共享库名称
    • 名字包含’/’:按照绝对路径或相对路径使用
    • 名字不包含’/’:按照连接器默认方式搜索共享库
  • int flags:选项
    • RTLD_LAZY:不解析共享库中未定义函数符号的内存地址,使用时再解析
    • RTLD_NOW:解析共享库库中未定义函数符号的内存地址,可用于调试
      :以上两个选项为必选选项,二选一
    • RTLD_GLOBAL:这个共享库中解析的函数符号可以被后面打开的库使用
    • RTLD_LOCAL:这个共享库中解析的函数符号不能被后面打开的库使用
      :以上两个选项为共享库的使用范围
    • RTLD_NODELETE:不卸载共享库,即使dlclose()后引用计数为0。此时,dlopen()不会重新初始化库中静态变量(glibc2.2可用)
    • RTLD_NOLOAD:不加载共享库,作用1:可用与测试共享库是否被加载,如果加载返回共享库句柄,不加载返回NULL。作用2:可再已打开的共享库上添加新的flags。(glibc2.2可用)
    • RTLD_DEEPBIND:解析库中符号引用时,先搜索库中的定义,如果库中没有,再搜索全局定义。(这个很有用,可无脑加上,glibc2.3.4可用)

dlclose() 关闭共享库

int dlclose(void *handle);
  • int返回值:成功返回0,失败返回非0
  • void *handle:共享库句柄

dlerror() 错误原因

const char *dlerror(void);
  • const char *返回值:如果dl系列函数出错,可调用dlerror()获取出错原因字符串

dlsym():获取符号的地址

void *dlsym(void *handle, char *symbol);
  • void *返回值:成功返回符号的地址,失败返回NULL
  • void *handle:共享库句柄
  • char *symbol:符号名称(函数名、变量名)

例子

root@jing-VirtualBox:~# ls
a.out  libt.so  main.c  t.c
root@jing-VirtualBox:~# cat t.c    #有点懒,并没有写t.c的头文件。。。
# include <stdio.h>

void f()
{
    printf("lib t\n");
}
root@jing-VirtualBox:~# cat main.c 
# include <stdio.h>
# include <dlfcn.h>

int main(void)
{
    void *hander = dlopen("./libt.so", RTLD_LAZY);
    if (hander == NULL) {
        printf("%s\n", dlerror());
    }

    void (*f)() = dlsym(hander, "f");
    f();  //调用函数f

    dlclose(hander);

    return 0;
}
root@jing-VirtualBox:~# gcc main.c -ldl
root@jing-VirtualBox:~# ./a.out 
lib t

共享库的设计

  • 因库可以动态加载,所以优秀共享库的.h文件,只包含对外可见的接口&变量。这样可以减少开销,防止程序员使用共享库私有的接口导致共享库升级时版本不兼容。

  • 使用static关键字修饰符号,表示符号私有于一个代码快,无法被其他.o文件使用。

链接器版本脚本

作用:确保连接器只导出指定符号。

gcc -shared a.o b.o c.o -o libabc.so -Wl,--version-script,scriptfile.map

-Wl,–version-script,scriptfile.map:指定一个脚本,规定可以导出的符号,脚本如下

VER_1 {
    global:    # 可以被加载的符号
        f_a;
        f_b;
        f_c;
    local:
        *;     #*表示除global之外的所有符号
};

共享库的初始化函数和终止函数

初始化函数会在共享库加载的时候自动执行,终止函数会在共享库卸载的时候自动执行,代码如下。

void __attribute__ ((constructor)) 初始化函数名(void)
{
    /*初始化代码*/
}

void __attribute__ ((destructor)) 终止函数名(void)
{
    /*终止代码*/
}

:以上均出自 Linux/UNIX系统编程手册,只选了感觉有用的。

猜你喜欢

转载自blog.csdn.net/vrg000/article/details/78139153