程序编译链接(六)-- 动态链接显示运行时链接

支持动态链接的系统往往都支持一种更加灵活的模块加载方式,叫做显示运行时链接,有时候也叫做运行时加载。
这种运行时加载使得程序的模块组织变得很灵活,可以用来实现一些诸如插件,驱动等功能。当程序需要用到某个插件或者驱动的时候,才将相应的模块装载进来,而不是需要从一开始就将他们全部装载进来,从而少了程序启动时间和内存使用。并且程序在运行的时候重新加载某个模块,这样使得程序本身不必重新启动而实现模块的增加,删除,更新等。

在Linux中,而动态库的装载则是通过一系列的API提供:dlopendlsymdlerrordclose

dlopen

dlopen函数负责打开一个动态库,并将其加载到进程进程的地址空间,完成初始化过程。

void* dlopen(const char* filename, int flag);
  • 第一个参数
    第一个参数是被加载动态库的路径,如果整个路径是绝对路径,则该函数将会尝试直接打开该动态库,如果是相对路径,那么将会按照下面的顺序查找:

    1. 查找有环境变量LD_LIBRARY_PATH指定的一系列目录
    2. 查找由/etc/ld.so.cache里面所指定的共享库路径
    3. /lib/usr/lib
  • 第二个参数
    表示函数符号的解析方式:

    • RTLD_LAZY
      表示使用延迟绑定,
    • RTLD_NOW
  • 返回值
    dlopen的返回值是被加载模块的句柄,如果加载失败则返回NULL。

dlsym
void* dlsym(void* handle, char *symbol);
  • 第一个参数
    dlopen返回的动态库的句柄。
  • 第二个参数
    即要查找的符号的名字。
  • 返回值
    如果dlsym找到了相应的符号,则返回该符号的值,如果查找的符号是一个函数,则返回函数的地址;
    如果是一个变量,则返回变量的地址。
    如果整个符号是一个常量,那么返回的是该常量的值。
dlerror

每次调用以后,都可以调用dlerror函数来判断上一次调用是否成功。

dlclose

dlclose的作用和dlopen相反,它的作用是将一个已经加载的模块卸载,系统会维持一个加载的引用计数器,每次使用dlopen加载模块时,相应的计数器加1,每次使用dlclose卸载模块时,相应计数器减1,只有当计数器值减少到0时,模块才真正被卸载掉。

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

int main(int argc, char **argv)
{
    void* handler;
    double (*func)(double);
    char* error;

    handler = dlopen(argv[1], RTLD_NOW);
    if (handler == NULL) {
        printf("Open library %s error: %s\n %s\n", argv[1], dlerror());
        return -1;
    }

    func = dlsym(handler, "sin");
    if ( (error = dlerror()) != NULL ) {
        printf("Symbol sin not found: %s\n", error);
        goto exit_runso;
    }

    printf("%f\n", func(3.1415926 / 2));

exit_runso:
    dlclose(handler);
}
# gcc RunSoSimple.c -o RunSoSimple -ldl  # -ldl 表示使用DL(Dynamical Loading)库
# ./RunSoSimple /lib64/libm-2.17.so
1.000000
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容