现在的位置: 首页 > 综合 > 正文

共享库的编译及使用

2014年01月17日 ⁄ 综合 ⁄ 共 2766字 ⁄ 字号 评论关闭

      linux下的库有两种:静态库和共享库。共享库,一般也被称为动态链接库。
      静态库的代码在编译过程中已经被载入可执行程序,因此体积较大,一般以libxxxx.a形式存在。
      共享库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小,一般以libxxxx.so的文件形式存在。
      库是别人写好的现有的,成熟的,可以复用的代码。实际项目开发中,每个程序都要依赖很多基础的底层库,不需要每个程序员去重新发明轮子,因此库的存在意义非同寻常。
      共享库的好处是,不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。
      ldd命令可以查看一个可执行程序依赖的共享库,

      例如:

 
      要自己编写一个动态链接库,以供多个进程或其他程序员使用,分为以下两步:
    (1)编写代码,包括头文件和源文件;
    (2)将一步中编写的源代码文件编译成一个动态库:libXXXX.so。
     下面通过一个例子来介绍如何生成一个动态库。这里有一个头文件:so_test.h,三个.c文件:test.c(实际项目中一般有更多头文件和源文件,我们稍后将这几个文件编译成一个动态库libtest.so),以及一个测试文件:test_main.c 。
     共享库头文件:
[cpp] view plaincopyprint?
#include "stdio.h"  
void test();  
    源文件:test.c:
[cpp] view plaincopyprint?
void MyTestFunction()  
{      
   printf("this is in test for shared object... ");  
}  

      最后,将以上文件编译成一个动态链接库libtest.so,以方便别人可以直接使用这三个接口:

     $ gcc test.c -fPIC -shared -olibtest.so
 
      到此为止,大功基本高成,动态链接库已经成功生成了。  

 前面我介绍了*.so文件的编译和生成。
          但对绝大多数的程序员,绝大多数时候来讲,我们更多的使用现有的共享库。例如上一节我们做好的:libtest.so。
          共享库的使用有2钟方式:
 1)一种是像静态库那样在编译时进行链接。即把动态链接库里的函数链接到我们现有程序。
  例如,我们写了一个程序,并且要使用到我们编译生成的libtest.so:
我们在代码中要做的,只需要把共享库头文件包括进来:
     Main.c:
[cpp] view plaincopyprint?
#include "so_test.h"                   /*把库的头文件include进来*/  
int main()  
  
{  
  <span style="color:#ff0000;"><strong>MyTestFunction</strong></span>();/*使用了动态链接库里的<span style="color:#ff0000;"><strong>MyTestFunction</strong></span>函数*/  
  return 0;  

}

gcc -o testvsid testvsid.c -ltos_dev_api -L /tos/so/  

(2)在运行时动态加载,实现类似插件的功能。要实现共享库的动态加载,必须用到系统提供的一组函数dlopen,dlsym,dlclose等。
1.dlopen 打开共享库
该函数搜索指定的共享库文件,如果该共享库还没有被加载到共享内存中,则加载到共享内存中,并且共享库的总引用数加一;
void *dlopen(const char *path, int mode);
其中,path是共享库的名称,mode是对标示符引用的处理方式,值可为RTLD_LAZY或RTLD_NOW,两者的区别是如果共享库中包含了另外一个共享库的引用,对于RTLD_LAZY模式会运行到引用那里时才会加载另一个共享库,而RTLD_LAZY模式则是加载共享库的时候就会加载另一个共享库。为了提高效率,一般用RTLD_LAZY模式。
该函数返回指向已打开的共享库的句柄指针,供其他函数使用,如果返回NULL表示调用失败。

2.dlsym 获得应用指针
该函数用来获得一个指向函数或数据结构的指针;
void * dlsym(void *handle, const char *symbol);
其中,handle是dlopen函数返回的句柄指针,symbol是函数名或外部数据结构名;
该函数返回指向要获取的函数或数据结构的指针(是(void *)类型,还要经过强制类型转换),如果返回NULL表示该函数或数据结构不存在。

3.dlclose 关闭共享库
该函数关闭已打开的共享库,将共享库的总引用数减一,如果总引用数降为0,则系统会从内存中卸载该共享库并释放资源。
int dlclose(void *handle);
其中,handle是dlopen函数返回的句柄指针;
该函数返回共享库的剩余的总引用数。

4.dlerror 返回错误信息
该函数用来获得dlopen和dlsym调用失败时的具体的错误信息;
const char * dlerror(void);
该函数返回一个字符串指针,指向产生的最后一个错误。

5._init 初始化函数
当dlopen第一次加载共享库时,如果共享库中存在_init()函数,则会自动调用该函数;
void _init(void);

6._fint 析构函数
当dlclose卸载共享库时,如果共享库中存在_fnit()函数,则会自动调用该函数;
void _fnit(void);

使用举例:
mytest.c
#include <stdio.h>;
#include <dlfcn.h>;

int main(void)
{
    void *dlh;
    int (*func)(void);
    
    dlh = dlopen("libtest.so", RTLD_LAZY);
    if (dlh == NULL)
    {
        printf("Load libtest.so fail, %s\n", dlerror());
        exit(1);
    }
    func = (int (*)(void))dlsym(dlh, "MyTestFunction");/*MyTestFunction是在libtest.so里提供和实现的*/
    if (func == NULL)
    {
        printf("Load symbol myfunction fail, %s\n", dlerror());
        exit(2);
    }
    func(); /*使用动态共享库里的函数*/
    fclose(dlh);
    exit(0);
}
复制代码

按如下编译:
对于Linux:    gcc  -o mytest  mytest.c ldl

抱歉!评论已关闭.