zoukankan      html  css  js  c++  java
  • 动态库和静态库

    很多时候我们需要多次调用一些函数,为了方便我们需要把这写函数封装在一个库里面,以便在以后主函数链接生成可执行文件的时候调用。因为是在主函数连接的时候利用,所以需要打包生成库文件的文件都必须是已经编译过的目标文件(即.o文件)。

    静态库:
        四个数学函数分别在不同的文件里面,但他们包含在一个头文件下面:
        math.h:

        #ifndef MATH_H_
        #define MATH_H_
        
        #include <stdio.h>
        
        typedef int(* funp)(int ,int );
        
        int add(int x,int y);
        int sub(int x,int y);
        int mul(int x,int y);
        int div(int x,int y);

        #enfif


    函数文件分别为:
        add.c

        #include "math.h"

        int add(int x,int y)
        {
            return x+y;

        }

    其余同理。

    主函数:
        #include "math.h"

        void callback(fun p,int x,int y,char *fname)
        {
            printf("%s=%d ",fname,p(x,y));
        }
        
        int main(void)
        {
            int a=9,b=3;
            callback(add,a,b,"add");
            callback(sub,a,b,"sub");
            callback(mul,a,b,"mul");
            callback(div,a,b,"div");
        
            return 0;
        }


    现在需要将数学函数都打包生成一个库函数,以便主函数直接调用。
    首先编译数学函数,得到目标文件,然后打包生成静态库文件。
        步骤如下:
    1:文件预处理:gcc -E add.c -o test.i
    .i文件中存放着.c预处理之后的代码,如果不加 -o test.i 的话不会输出预处理结果,    看不到.i文件。
    2:文件编译:gcc -S add.i -o add.s
    预处理之后,可直接对生成的.i文件编译,生成汇编代码:
    3:汇编:gcc -c add.s -o add.o
    汇编器将汇编代码编译为目标文件.o

    /*因为我们的目的是打包生成库函数,是供主函数调用时链接生成可执行文件的,所以只要生成目标文件就可以了。但是主函数要生成可执行文件,除了上面3个步骤外,还需要一个链接的过程。就是将主函数生成的目标文件和附加的目标文件链接起来,最终生成可执行文件。例如:gcc - main.o main */

    当生成所有需要的.o文件后,就需要打包生成静态库文件了
    在命令行中输入:
    ar rs libmymath.a add.o sub.o mul.o div.o
    库文件名都是以lib开头的,静态库以.a作为后缀(archive),ar命令类似以tar命令,用来打包的,但是把目标文件打包生成静态库文件只能用ar命令。选项r表示将后面的文件列表添加到文件包.a当中,如果.a不存在就创建,存在就更新。s表示为静态库创建索引,给链接器使用。ranlib也可以为静态可创建索引,上面命令等价于:
    ar r libmymath.a add.o sub.o mul.o div.o
    ranlib libmymath.a
    系统就会提示创建一个静态库文件。这样静态库就做好了。


    然后我们编译主函数的时候把库文件链接上:
    gcc main.c -L库文件路径 -l库文件名(文件名数除去前后缀的部分,本例中就是mymath) -I头文件路径 -o main

    -L选项告诉编译器到哪里寻找库文件,-l告诉编译器要连接哪个库,-I告诉编译器去哪里寻找头文件。注意,即使库文件就在当前目录,编译器也不会去找,所以-L选项不能少。

    由于利用指令的方法创建库函数很麻烦,下面介绍一种简单的方法:
    用Makefile创建静态库函数

    将静态库函数需要包含的目标文件的源文件.c放到一个文件夹当中,在此文件夹中创建Makefile文件(vim Makefile)

    #Makefile

    libmymath.a:*.c
        gcc -c *.c
        ar rs libmymath.a *.o

    clean:
        @echo "cleanning project"
        -rm *.o
        -rm *.a
        @echo "clean completed"

    .PHONY :clean

    由于Makefile的隐含规则中可以默认当没有源文件时自动生成源文件,所以Makefile可以简化为
    #Makefile

    libmymath.a:*.o
        ar rs libmymath.a *.o

    clean:
        @echo "cleanning project"
        -rm *.o
        -rm *.a
        @echo "clean completed"

    .PHONY :clean

    当在当前路径下的命令行中输入make时就可创建静态库函数,同理可以再主函数文件夹下面写一个Makefile,也可以完成。
        




    动态库:

    生成动态库也需要目标文件,但是和一般地目标文件有所不同(具体原因待补),在编译时需要加-fPIC选项,表示-f后面编译的选项生成的是位置无关代码。
    例如:
    gcc -c -fPIC add.c sub.c mul.c div.c
    这样生成目标文件了,之在打包生成动态库:
    gcc -shared -o libmymath.so *.o
    这样就生成了动态库了。

    这样可以一步完成:gcc -shared -fPIC -o libmymath.so  *.c


    现在把main.c和共享库编译连接在一起:
    gcc main.c -L库文件位置 -l库文件名 -I头文件位置 -o main
    然后运行:
    ./main
    结果出乎意料,编译时没问题,由于指定了-L选项,编译器可以找到库文件,而运行时却说找不到。那么运行时在哪些路径下找共享库呢?可以用ldd命令查看:
    ldd mian

    共享库路径的搜索顺序:
    1,在环境变量LD_LIBRARY_PATH保存的路径中查找。
    2,然后从缓存文件/etc/ld.so.cache中查找。
    3,如果上述步骤都没找到,则到默认的系统库文件目录中查找,先是/lib然后是/usr/lib。

    既然如此,我们在运行程序时修改环境变量,把共享库所在的目录添加到搜索路径:
    LD_LIBRARY_PATH=/home/...(库文件的路径)  ./main
    也可以用这样两条命令:
    export LD_LIBRARY_PATH=/home/...(库文件路径,可以用$PWD表示在当前路径)
    ./main
    第一条命令在当前shell进程中设置一个环境变量,一旦在shell进程中设置了环境变量,以后每次执行时shell都会把自己的环境变量传给新创建的进程,所以第二条命令创建的进程main就会获得这个环境变量。但是在关闭shell,重启一遍
    terminal时这个新设置的环境变量就无效了。注意这两种执行方式的区别,第一种 LD_LIBRARY_PATH=/home/...(库文件的路径)  ./main 命令室友当前创建的main进程才获得这个环境变量,shell本身不保存,以后执行其他命令也不会获得它,所以重新执行./main时找不到共享库,但是第二种方法在不关闭shell的前提下重新执行./main是有效的。

    另外一种方法就是把libmymath.so 考到/usr/lib或者/lib目录,这样可以确保动态链接器能找到这个共享库


    用Makefile的方法完成共享库的创建和链接:

    #makefile

    libmymath.so;*.c
        gcc -c *.c -fPIC
        gcc -shared -o libmymath.so *.o

    install:
        sudo cp libmymath.so /usr/lib
    remove:
        sudo rm /usr/lib/libmymath.so

    clean:
        @echo "clean project"
        -rm *.o
        @echo "clean completed"
    distclean:
        @echo "clean project"
        -rm libmymath.so
        @echo "clean completed"

    .PHONY:clean
    .PHONY:distclean

    利用隐含规则可以简化:
    #makefile
    CFLAGS=--fPIC

    libmymath.so;*.o
        gcc -shared -o libmymath.so *.o

    install:
        sudo cp libmymath.so /usr/lib
    remove:
        sudo rm /usr/lib/libmymath.so

    clean:
        @echo "clean project"
        -rm *.o
        @echo "clean completed"
    distclean:
        @echo "clean project"
        -rm libmymath.so
        @echo "clean completed"

    .PHONY:clean
    .PHONY:distclean

    在主函数文件夹里面写下Makefile:
    #Makefile
    main:mian.o
        make -C 动态库文件的路径
        gcc main.c -L动态库文件路径 -l动态库文件名 -I头文件路径 --o main

        clean:
        @echo "clean project"
        -rm *.o
        @echo "clean completed"

    distclean:
        @echo "clean project"
        -rm main
        @echo "clean completed"

    这样在在库文件目录下make,然后make install安装完成后,在主函数文件目录下make命令就生成了main可执行文件。然后在库文件目录下make remove即可卸载安装。

    1,使用dlopen()打开指定的动态链接库文件,

    2,使用dlsym()查找库文件中的函数,并赋值给一个函数指针变量,

    3,使用函数指针变量运行对应的函数,

    4,使用dlclose关闭打开的动态链接库文件,

    编译命令:gcc -o testdlso testdlso.c -ldl

    运行:  ./testdlso fun.so

     http://www.cnblogs.com/Anker/p/3746802.html

    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 
      成功,返回库引用,指向动态库文件的void指针
      编译时候要加入 -ldl (指定dl库) 
      例如 
      gcc test.c -o test -ldl
    编辑本段
    使用 dlopen
       dlopen()是一个强大的库函数。该函数将打开一个新库,并把它装入内存。该函数主要用来加载库中的符号,这些符号在编译的时候是不知道的。比如 Apache Web 服务器利用这个函数在运行过程中加载模块,这为它提供了额外的能力。一个配置文件控制了加载模块的过程。这种机制使得在系统中添加或者删除一个模块时,都 不需要重新编译了。 
      可以在自己的程序中使用 dlopen()。dlopen() 在 dlfcn.h 中定义,并在 dl 库中实现。它需要两个参数:一个文件名和一个标志。文件名可以是我们学习过的库中的 soname。标志指明是否立刻计算库的依赖性。如果设置为 RTLD_NOW 的话,则立刻计算;如果设置的是 RTLD_LAZY,则在需要的时候才计算。另外,可以指定 RTLD_GLOBAL,它使得那些在以后才加载的库可以获得其中的符号。 

      当库被装入后,可以把 dlopen() 返回的句柄作为给 dlsym() 的第一个参数,以获得符号在库中的地址。使用这个地址,就可以获得库中特定函数的指针,并且调用装载库中的相应函数。

    NOTE: 如果pathname是NULL, 则返回的是调用dlopen的模块的库引用(handle)。 例如: 如果我们在我们的APP里面调用dlopen去动态load一个.so, 但是pathname传入的是NULL, 则dlopen最后返回的应该是该APP的引用(handle)

    Additional link: http://tldp.org/HOWTO/Program-Library-HOWTO/dl-libraries.html

    --------------------------------------------------------------------------------------------------------------------------

    dlsym
       
      dlsym()的函数原型是 
      void* dlsym(void* handle,const char* symbol) 
      该函数在<dlfcn.h>文件中。 
      handle是由dlopen打开动态链接库后返回的指针,symbol就是要求获取的函数的名称,函数返回值是void*,指向函数的地址,供调用使用

    取动态对象地址:
    #include <dlfcn.h>
    void *dlsym(void *pHandle, char *symbol);
    dlsym根据动态链接库操作句柄(pHandle)与符号(symbol),返回符号对应的地址。
    使用这个函数不但可以获取函数地址,也可以获取变量地址。比如,假设在so中
    定义了一个void mytest()函数,那在使用so时先声明一个函数指针:
    void (*pMytest)(),然后使用dlsym函数将函数指针pMytest指向mytest函数,
    pMytest = (void (*)())dlsym(pHandle, "mytest");

    --------------------------------------------------------------------------------------------------------------------------

    dlclose
      dlclose() 
      包含头文件: 
      #include <dlfcn.h> 
      函数原型为: 
      int dlclose (void *handle); 
      函数描述: 
       dlclose用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。

    --------------------------------------------------------------------------------------------------------------------------

    dlerror
      dlerror() 
      包含头文件: 
      #include <dlfcn.h> 
      函数原型: 
      const char *dlerror(void); 
      函数描述: 
      当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。

    LINUX创建与使用动态链接库并不是一件难事。
      编译函数源程序时选用-shared选项即可创建动态链接库,注意应以.so后缀命名,最好放到公用库目录(如/lib,/usr/lib等)下面,并要写好用户接口文件,以便其它用户共享。
      使用动态链接库,源程序中要包含dlfcn.h头文件,写程序时注意dlopen等函数的正确调用,编译时要采用-rdynamic选项与-ldl选项 ,以产生可调用动态链接库的执行代码

    EXAMPLE

    #include <stdio.h>

    int add(int a, int b)
    {
            return a+b;
    }
    int sub(int a, int b)
    {
            return a-b;
    }

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

    typedef int (* funp)(int ,int );

    int main(void)
    {
            void *handle;                                                             /*用来标定库文件加载的内存位置*/

            handle = dlopen("./libmath.so", RTLD_LAZY);      /*根据库文件的路径加载库文件到内存中*/
            if(!handle){
                    dlerror();
            }

            funp fp  = (int (*)(int ,int ))dlsym(handle, "add");    /*根据函数名符号找到需要动态加载的函数位置*/
            if(!fp){
                    dlerror();
            }
            printf("add = %d ",fp(3,4));

            if(dlclose(handle)){                    /*从内存中卸载动态库文件*/
                    dlerror();
            }

            return 0;
    }

    编译:gcc main.c -ldl

    注意:事先要把动态库文件准备好。

  • 相关阅读:
    Java正则表达式入门
    StringBuffer ,String,StringBuilder的区别
    JAVA的StringBuffer类
    容器vector的使用总结 容器stack(栈)
    c++网络通信(与服务器通信聊天)和c#网络通信
    C#与SQLite数据库
    我的vim配置文件
    在world中批量调整图片的大小
    C#判断文件及文件夹是否存在并创建(C#判断文件夹存在)
    C# Thread类的应用
  • 原文地址:https://www.cnblogs.com/yiyutianran/p/3763611.html
Copyright © 2011-2022 走看看