zoukankan      html  css  js  c++  java
  • linux 静态库、共享库

    一、什么是库
     
    本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。由于windows和linux的本质不同,因此二者库的二进制是不兼容的。
     
    Linux操作系统支持的函数库分为静态库和动态库,动态库又称共享库。Linux系统有几个重要的目录存放相应的函数库,如/lib    /usr/lib。
     
    二、静态函数库、动态函数库
     
    A.  这类库的名字一般是libxxx.a;利用静态函数库编译成的文件比较大,因为整个函数库的所有数据都被整合进目标代码中,他的优点就显而易见了,即编译后的执行程序不需要外部的函数库支持,因为所有使用的函数都已经被编译进可执行文件了。当然这也会称为它的缺点,因为如果静态函数库改变了,那么你的程序必须重新编译,而且体积也较大。
     
    B.这类库德名字一般是libxxx.so,动态库又称共享库;相对于静态函数库,动态函数库在编译的时候并没有被编译进目标代码中,你的程序执行到相关函数时才调用函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。由于函数库没有被整合进你的程序,而是程序运行时动态申请并调用,所以程序的运行环境中必须提供相应的库。动态函数库的改变并不影响你的程序,所以动态函数库的升级比较方便。而且如果多个应用程序都要使用同一函数库,动态库就非常适合,可以减少应用程序的体积。
     
    注意:不管是静态函数库还是动态函数库,都是由*.o目标文件生成。
     
    三、函数库的创建
     
    A.静态函数库的创建
     
    ar -cr  libname.a   test1.o  test2.o
     
    ar:静态函数库创建的命令
    -c :create的意思
    -r :replace的意思,表示当前插入的模块名已经在库中存在,则替换同名的模块。如果若干模块中有一个模块在库中不存在,ar显示一个错误信息,并不替换其他同名的模块。默认的情况下,新的成员增加在库德结尾处。
     
    B.动态函数库的创建
     
    gcc -shared  -fpic  -o libname.so  test1.c test2.c
     
    -fpic:产生代码位置无关代码



    -shared :生成共享库
     
    四、静态库和动态库的使用 
     
    案例:
     
    add.c
    #include
     
    int add(int a,int b)
    {
    return a + b;
    }
    sub.c
    #include
     
    int sub(int a,int b)
    {
    return a - b;
    }
     
    head.h
     
    #ifndef _HEAD_H_
    #define _HEAD_H_
    extern int add(int a,int b);
    extern int sub(int a,int b);
    #endif
    main.c
    #include
     
    int main(int argc,char *argv[])
    {
    int a,b;
     
    if(argc < 3)
    {
    fprintf(stderr,"Usage : %s argv[1] argv[2]. ",argv[0]);
    return -1;
    }
     
    a = atoi(argv[1]);
    b = atoi(argv[2]);
     
    printf("a + b = %d ",add(a,b));
    printf("a - b = %d ",sub(a,b));
     
    return 0;
    }
    生成静态库



    生成动态库:



    使用生成的生成的库:



    其中
    -L 指定函数库查找的位置,注意L后面还有'.',表示在当前目录下查找
    -l则指定函数库名,其中的lib和.a(.so)省略。
     
    注意:-L是指定查找位置,-l指定需要操作的库名。
     
    从上面的运行结果中,我们可以看到:
     
    A.当动态库和静态库同时存在的时候,gcc默认使用的是动态库。如果强制使用静态库则需要加-static选项支持。
    B.动态库生成的可执行文件,test1不能正常的运行。
    C.链接静态库的可执行程序明显比链接动态库的可执行文件大。
     
    五、让链接动态库的可执行程序正常运行。
     
    当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路劲。此时就需要系统动态载入器(dynamic  linker/loader)。
     
    对于elf格式的可执行程序,是由ld-linux.so*来完成的,它先后搜索elf文件的DT_RPATH段---环境变量LD_LIBRARY_PATH、/etc/ld.so.cache文件列表、/usr/lib、/lib目录找到库文件后将其载入内存。
     
    A.一种最直接的方法,就是把生成的动态库拷贝到/usr/lib或/lib中去。

    B.使用LD_LIBRARY_PATH环境变量,这个环境变量在ubuntu操作系统中默认没有,需要手动添加



    C.动态在安装在其他目录下,如果想操作系统能找到它,可以通过一下步骤
     
    <1>新建并编辑/etc/ld.so.conf.d/my.conf文件,加入库所在目录的路径
    <2>执行ldconfig命令更新ld.so.cache文件


    此时,在执行链接动态库的可执行文件则可以正常运行。
     
    六、查看库中的符号
     
    A.nm命令可以打印出库中涉及到的所有符号。库既可以是静态库也可以是动态的。
     
    常见的三种符号:
    <1>在库中被调用,但没有在库中定义(表明需要其他库支持),用U表示
    <2>在库中定义的函数,用T表示
    <3>“弱态”符号,他们虽然在库中被定义,但是可能被其他库中同名的符号覆盖,用W表示。



    B.ldd命令可以查看一个可执行程序依赖的共享库



    七、动态加载库
     
    用gcc -shared生成的我们称为动态库(共享库),其中动态库在运行的过程中有两种方式
     
    A.动态链接
     
    这种方式下,可执行程序只是做一个动态的链接,当需要用到动态库中的函数时,有加载器隐士的加载。
     
    B.动态加载
     
    这种方式下,在可执行程序的内部,我们可以用dlopen()这样的函数,手动进行加载,dlsym()函数找到我们想要调用函数的入口地址,然后进行调用。这种方式,在写插件程序中得到广泛应用。
     
    相关的API:


    <1>dlopen()打开一个新的动态库,并把它装入内存。该函数主要用来记载库中的符号,这些符号在编译的时候是不知道的。
     
    dlopen()函数需要两个参数:一个文件名和一个标志。
     
    A.文件名是我们之前接触过的动态库的名字,如果它是一个绝对路径,如:/home/cyg/worddir/libname.so,此时dlopen直接到指定的路径下打开动态库。
    如果没有指定路径,仅仅指定了一个动态库的名字,此时dlopen将它先后搜索elf文件的DT_RPATH段、环境变量LD_LIBRARY_PATH、/etc/ld.so.cache文件列表、/lib、/usr/lib目录找到库文件后将其载入内存。
     
    B.标志指明是否立刻计算库的依赖性。

    常常一个库中还依赖别的库,就是这个函数实现的时候,调用了别的库函数。不是在这个库中实现的函数我们称为位定义的符号。
     
    如果将标志 设置为RTLD_NOW的话,则会在dlopen函数返回前,将这些未定义的符号解析出来。如果设置为RTLD_LAZY,则会在需要的时候才会去解析。
     
    返回值:dlopen()函数会返回一个句柄作为dlsym()函数的第一个参数,以获得符号在库中的地址。使用这个地址,就可以获得库中特定函数的指针,并且调用装载库中的相应函数。
     
    <2>dlerror()
     
    当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。
     
    <3>void *dlsym(void *handle,char *symbol);
     
    dlsym根据动态链接库操作句柄(handle)与符号(symbol),返回符号对应的函数的执行代码地址。由此地址,可以带参数执行相应的函数。
     
    如程序代码 :int (*add)(int x,int y);//函数指针
     
    handle = dlopen("xxx.so",RTLD_LAZY);//打开共享库
    add = dlsym(handle,"add");//获取add函数在共享库的地址
    value = add(12,34);//调用add函数
     
    <4>int dlclose(void *handle);
    dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。
     
    案例:
    #include
    #include
    #include
    #include
     
    int test_dl(char *pso,char *pfu)
    {
    void *handle;
    int (*ptest)(int x,int y);
     
    if((handle = dlopen(pso,RTLD_LAZY)) == NULL)
    {
    printf("%s. ",dlerror());
    return -1;
    }
     
    if((ptest = dlsym(handle,pfu)) == NULL)
    {
    printf("%s. ",dlerror());
    return -1;
    }
     
    printf("ptest complete : %d. ",ptest(12,13));
     
    dlclose(handle);
     
    return 0;
    }
     
    int main(int argc,char *argv[])
    {
    char buf[100];
    char *pso,*pfun;
     
    printf("Input xxx.so:function_name. ");
    while(1)
    {
    printf(">");
    fgets(buf,sizeof(buf),stdin);
    buf[strlen(buf)-1] = 0;
     
    pso = strdup(strtok(buf,":"));
    pfun = strdup(strtok(NULL,":"));
     
    test_dl(pso,pfun);
    }
     
    return 0;
    }
     
    运行结果:

     
  • 相关阅读:
    java的构造方法 java程序员
    No result defined for action cxd.action.QueryAction and result success java程序员
    大学毕业后拉开差距的真正原因 java程序员
    hibernate的回滚 java程序员
    验证码 getOutputStream() has already been called for this response异常的原因和解决方法 java程序员
    浅谈ssh(struts,spring,hibernate三大框架)整合的意义及其精髓 java程序员
    你平静的生活或许会在某个不可预见的时刻被彻底打碎 java程序员
    Spring配置文件中使用ref local与ref bean的区别. 在ApplicationResources.properties文件中,使用<ref bean>与<ref local>方法如下 java程序员
    poj1416Shredding Company
    poj1905Expanding Rods
  • 原文地址:https://www.cnblogs.com/ostin/p/9194597.html
Copyright © 2011-2022 走看看