zoukankan      html  css  js  c++  java
  • 系统程序员成长计划-算法与容器(三) (下)


    转载时请注明出处和作者联系方式
    文章出处:http://www.limodev.cn/blog
    作者联系方式:李先静 <xianjimli at hotmail dot com>

    在继续学习之前,我们先介绍几个概念:

    静态库:在Linux下,静态库的扩展名为.a,a代表archive的意思。正常情况下一个C源文件编译之后生成一个目标文件(.o),目标文件 里存放的是程序的机器指令和数据,它不包含运行时的信息,所以不能直接运行。用命令ar把多个目标文件打包成一个文件就生成了所谓的静态库。可执行文件链 接静态库时,会把用到的函数和数据拷贝过去。多个可执行文件链接同一个静态库时,所用到的函数和数据就会拷贝多次,那就存在不少空间浪费。

    共享库:顾名思义,共享库可以在多个可执行文件之间共享,链接时不用拷贝函数或数据,只是建立一个函数链接表(PLT),在运行时通过这个表来确定 具体调用的函数。共享库可以有效的避免空间浪费,但它也不是免费的午餐,它在加载时存在额外的开销,在链接多个共享库时会比较明显。不过目前出现的 prelink和gnu hash等技术,有效的缓解了这个问题。共享库另外一个好处就是可以单独升级,理论上,修改某个共享库不需要重新编译依赖它的应用程序(不过实际操作时与 它的接口变化和信息隐藏程度有关)。

    可执行文件:Linux下加x属性的文件都是可执行文件,这里可执行文件特指ELF(Executable and Linking Format)格式的可执行文件。 可执行文件在编译时连接静态库,会把所用到的函数会被拷贝过来,运行时不再依赖于静态库。在编译时链接共享库,它不拷贝函数和数据,但在运行时会依赖于其 链接的共享库。

    回到前面的问题:我们发现在编译时,无论是链接静态库还是共享库,它都绑定了调用者与使用者之间的关系。真正要在运行时决定而不是编译时决定使用哪种容器,那就不能包含具体容器的头文件,链接具体容器所在的库了。今天我们要学习一种新的技术:在运行时动态加载共享库。

    除了一些嵌入式环境下使用的实时操作系统(RTOS)外,现代操作系统都支持在运行时动态加载共享库的机制。Linux下有dlopen系列函数,Windows下有LoadLibrary系列函数,其它平台也有类似的函数。下面是使用dlopen的简单示例:

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

    int main(int argc, char **argv)
    {
    void *handle = NULL;
    double (*cosine)(double) = NULL;
    /*加载共享库*/
    handle = dlopen("libm.so", RTLD_LAZY);
    /*通过函数名找到函数指针*/
    *(void **) (&cosine) = dlsym(handle, "cos");
    /*调用函数*/
    printf("%f/n", (*cosine)(2.0));
    /*卸载共享库*/
    dlclose(handle);

    return 0;
    }

    由于这些函数在每个平台的名称和参数都有所不同,直接使用这些函数会带来可移植性的问题,为此有必要对它们进行包装。不同平台有不同的函数,也就是 说存在多种不同的实现,那这是否意味着要用接口呢?答案是不用。原因是同一个平台只一种实现,而且不会出现潜在的变化。我们要做的只是加一个适配层,用它 来隔离不同平台就好了。

    我们把这个对加载函数的适配层称为Module,声明(module.h)如下:

    struct _Module;
    typedef struct _Module Module;

    typedef enum _ModuleFlags
    {
    MODULE_FLAGS_NONE,
    MODULE_FLAGS_DELAY = 1
    }ModuleFlags;

    Module* module_create(const char* file_name, ModuleFlags flags);
    void* module_sym(Module* thiz, const char* func_name);
    void module_destroy(Module* thiz);

    这里它们的实现只是对dl系列函数做个简单包装。由于不同平台有不同的实现,为了维护方便,我们把不同的实现放在不同的文件中,比如Linux的实现放在module_linux.c里。

    Module* module_create(const char* file_name, ModuleFlags flags)
    {
    Module* thiz = NULL;
    return_val_if_fail(file_name != NULL, NULL);

    if((thiz = malloc(sizeof(Module))) != NULL)
    {
    thiz->handle = dlopen(file_name, flags & MODULE_FLAGS_DELAY ? RTLD_LAZY : RTLD_NOW);
    if(thiz->handle == NULL)
    {
    free(thiz);
    thiz = NULL;
    printf("%s/n", dlerror());
    }
    }

    return thiz;
    }

    我们再看看队列的测试程序怎么写:

    #include "linear_container.h" 

    typedef LinearContainer* (*LinearContainerDarrayCreateFunc)(DataDestroyFunc data_destroy, void* ctx);

    int main(int argc, char* argv[])
    {
    int i = 0;
    int n = 1000;
    int ret_data = 0;
    Queue* queue = NULL;
    Module* module = NULL;
    LinearContainerDarrayCreateFunc linear_container_create = NULL;
    if(argc != 3)
    {
    printf("%s sharelib linear_container_create/n", argv[0]);

    return 0;
    }

    module = module_create(argv[1], 0);
    return_val_if_fail(module != NULL, 0);
    linear_container_create = (LinearContainerDarrayCreateFunc)module_sym(module, argv[2]);
    return_val_if_fail(linear_container_create != NULL, 0);

    queue = queue_create(linear_container_create(NULL, NULL));
    ...
    }

    说明:
    o 头文件只包含linear_container.h,而不包含linear_container_dlist.h。
    o 通过module_sym获取容器的创建函数,而不是直接调用linear_container_dlist_create。

    编译时不再链接容器所在的共享库:
    gcc -Wall -g module_linux.c queue.c queue_test.c -ldl -o queue_dynamic_test

    运行决定要使用的容器:
    ./queue_dynamic_test ./libcontainer.so linear_container_dlist_create

    这样一来,容器的调用者和使用者完全分隔开了。接口+动态加载的方式也称为插件式设计,它是软件可扩展性的基础,像操作系统、办公软件、浏览器和WEB服务器等大型软件都无一例外的使用了这类技术。

    本节示例代码请到这里下载。

  • 相关阅读:
    fopen flock fclose 文件用法
    thinkphp并发 阻塞模式与非阻塞模式
    thinkphp3.2 控制器导入模型
    thinkphp3.2 session时间周期无效
    UWP滑动后退
    旺信UWP公测邀请
    旺信UWP倒计时
    UWP应用开发系列视频教程简介
    新浪微博UWP UI意见征求
    淘宝UWP--自定义图片缓存
  • 原文地址:https://www.cnblogs.com/zhangyunlin/p/6167546.html
Copyright © 2011-2022 走看看