Chinaunix首页 | 论坛 | 博客
  • 博客访问: 216675
  • 博文数量: 37
  • 博客积分: 3082
  • 博客等级: 中校
  • 技术积分: 387
  • 用 户 组: 普通用户
  • 注册时间: 2006-03-14 13:59
文章分类
文章存档

2013年(1)

2012年(1)

2011年(2)

2010年(7)

2009年(1)

2008年(13)

2007年(12)

我的朋友

分类: LINUX

2008-02-22 19:25:14

Linux库知识
linux库的知识(概)

引言:
在xmeeting中,关于usb手柄部分,采用动态库调用方式,下面翻译一篇David A. Wheeler写的文章。文章就如何创建和使用静态库,共享库以及动如何动态装载库进行了论述。内容纲要如下:
 1.概述
 2.静态库
 3.共享库
   3.1 约定
       3.2 使用
    3.3 环境变量
   3.4 创建共享库
   3.5 安装与使用
   3.6 兼容性
 4.动态加载
   4.1 dlopen()
   4.2 dlerror()
   4.3 dlsym()
   4.4 dlclose()
   4.5 示例
 5.辅助知识
   5.1 nm命令
   5.2 库的构建与析构函数
   5.3 脚本
   5.4 版本
   5.5 GNU libtool
    5.6 去除符号空间
   5.7 外部执行体
    5.8 C++ 与 C
   5.9 加速C++初始化
   5.10 Linux标准
1.概述
  本文就如何在Linux系统中运用GNU工具创建和使用程序库进行论述。所谓"程序库",简单说,就是包含了数据和执行码的文件。其不能单独执行,可以作为其它执行程序的一部分,来完成执行功能。库的存在,可以使得程序模块化,可以加快程序的再编译,可以实现代码重用,可以使得程序便于升级。程序库可分三类:静态库,共享库和动态加载库。

  静态库,是在执行程序运行前就已经加入到执行码中,在物理上成为执行程序的一部分;共享库,是在执行程序启动时加载到执行程序中,可以被多个执行程序共享使用。动态加载库,其实并不是一种真正的库类型,应该是一种库的使用技术,应用程序可以在运行过程中随时加载和使用库。
  建议库开发人员创建共享库,比较明显的优势在于库是独立的,便于维护和更新;而静态库的更新比较麻烦,一般不做推荐。然而,它们又各有优点,后面会讲到。在C++编程中,要使用动态加载技术,需要参考文章"C++ dlopen MINI-Howto"。
  文章中讲述的执行程序和库都采用ELF(Executable and Linking Format)格式,尽管GNU GCC 工具可以处理其它格式,但不在本文的讨论范围。本文可以在::URL::> libpng12.so
  lrwxrwxrwx  1 root root  libpng.so.2 ->   libpng.so.2.1.0.12
  -rw-r--r--  1 root root  libpng.so.2.1.0.12
  在以上信息中,  libpng.so.2.1.0.12是共享库的实名(real name),libpng.so.2是共享库搜名(soname),libpng.so 则是连接名(linker name),用于编译连接。

3.2共享库的装载
  在所有基于GNU glibc的系统(当然包括Linux)中,在启动一个ELF二进制执行程序时,一个特殊的程序"程序装载器"会被自动装载并运行。在linux中,这个程序装载器就是/lib/ld-linux.so.X(X是版本号)。它会查找并装载应用程序所依赖的所有共享库。
  被搜索的目录保存在/etc/ls.so.conf文件中,但一般/usr/local/lib并不在搜索之列,至少debian是这样。这似乎是一个系统失误,只好自己加上了。
  当然,如果程序的每次启动,都要去搜索一番,势必效率不堪忍受。Linux系统已经考虑这一点,对共享库采用了缓存管理。ldconfig就是实现这一功能的工具,其缺省读取/etc/ld.so.conf文件,对所有共享库按照一定规范建立符号连接,然后将信息写入/etc/ld.so.cache。 /etc/ld.so.cache的存在大大加快了程序的启动速度。

3.3创建共享库
  共享库的创建比较简单,基本有两步。首先使用-fPIC或-fpic创建目标文件,PIC或pic表示位置无关代码,然后就可以使用以下格式创建共享库了:
 gcc -share _Wl,-soname,your_soname -o library_name file_list library_list
  下面是使用a.c和b.c创建库的示例:
   gcc -fPIC -g -c -Wall a.c
   gcc -fPIC -g -c -Wall b.c
   gcc -share -Wl,-soname, libmyab.so.1 -o libmyab.so.1.0.1 a.o b.o -lc
   -g表示带有调试信息,-Wall表示产生警告信息。
  几个需要注意的地方:
  (1)不推荐使用strip处理共享库,最好不要使用-fomit-frame-pointer编译选项
  (2)-fPIC和-fpic都可以产生目标独立代码,一般采用-fPIC,尽管其产生的目标文件可能会大些;-fpic产生的代码小,执行速度快,但可能有平台依赖限制。
  (3)一般情况下,-Wall,-soname,your_soname编译选项是需要的。当然,-share选项更不能丢。

4 动态加载库
  DL技术可以允许应用程序在运行过程的任何时候去加载和使用指定的库。这一技术在插件的实现上很实用。动态加载库这一概念并不是着眼于库的文件格式,而是指使用方式。存在着一组接口函数,使得应用程序可以采用DL技术。下面对这些接口函数逐一介绍,在最后给出应用示例。

4.1 dlopen
  函数原型:void *dlopen(const char *libname,int flag);
  功能描述:dlopen必须在dlerror,dlsym和dlclose之前调用,表示要将库装载到内存,准备使用。如果要装载的库依赖于其它库,必须首先装载依赖库。如果dlopen操作失败,返回NULL值;如果库已经被装载过,则dlopen会返回同样的句柄。
  参数中的libname一般是库的全路径,这样dlopen会直接装载该文件;如果只是指定了库名称,在dlopen会按照下面的机制去搜寻:
  (1)根据环境变量LD_LIBRARY_PATH查找
  (2)根据/etc/ld.so.cache查找
  (3)查找依次在/lib和/usr/lib目录查找。
  flag参数表示处理未定义函数的方式,可以使用RTLD_LAZY或RTLD_NOW。RTLD_LAZY表示暂时不去处理未定义函数,先把库装载到内存,等用到没定义的函数再说;RTLD_NOW表示马上检查是否存在未定义的函数,若存在,则dlopen以失败告终。

4.2 dlerror
  函数原型:char *dlerror(void);
  功能描述:dlerror可以获得最近一次dlopen,dlsym或dlclose操作的错误信息,返回NULL表示无错误。dlerror在返回错误信息的同时,也会清除错误信息。

4.3 dlsym
  函数原型:void *dlsym(void *handle,const char *symbol);
  功能描述:在dlopen之后,库被装载到内存。dlsym可以获得指定函数(symbol)在内存中的位置(指针)。如果找不到指定函数,则dlsym会返回NULL值。但判断函数是否存在最好的方法是使用dlerror函数,下面是示例:
  dlerror();/*清除错误信息*/
  function = dlsym(handle,"function_name");
  if((error=dlerror()) != NULL)
  {
    /*错误处理*/
  }
  else
  {
    /*找到函数*/
  }

4.4 dlclose
  函数原型:int dlclose(void *);
  功能描述:将已经装载的库句柄减一,如果句柄减至零,则该库会被卸载。如果存在析构函数,则在dlclose之后,析构函数会被调用。

4.5动态加载库示例
#include 
#include 
#include 

int main(int argc,char **argv)
{
  void *handle;
  double (*cosine)(double);
  char *error;

  handle = dlopen("/lib/libm.so.6",RTLD_LAZY);
  if(!handle)
  {
    printf("%s\n",dlerror());
    exit(1);
  }

  printf("opened /lib/libm.so.6\n");

  cosine = dlsym(handle,"cos");
  if((error = dlerror()) != NULL)
  {
    printf("%s\n",error);
    dlclose(handle);
    printf("after error,closed /lib/libm.so.6\n");
    exit(1);
  }

  printf("%f\n",(*cosine)(2.0));

  dlclose(handle);
  printf("closed /lib/libm.so.6\n");

  return 0;
}
  编译:gcc -o test test.c -ldl。在这个例子中,/lib/libm.so.6是动态加载库,而/usr/lib/libdl.so则是共享库。


5.相关知识
5.1 nm命令
  nm(1)命令可以报告库的符号列表,对于查看库的相关信息是一个不错的工具。具体使用查看帮助文档。示例:
$nm -D libavcodec-0.4.7.so | grep 263
结果如下:
00116438 T ff_clean_h263_qscales
00122d14 T ff_h263_decode_end
001223d4 T ff_h263_decode_frame
00121340 T ff_h263_decode_init
0011048c T ff_h263_decode_mb
0011652c T ff_h263_get_gob_height
0010e664 T ff_h263_resync
00041744 T ff_h263_round_chroma
0010810c T ff_h263_update_motion_val
00115f64 T flv_h263_decode_picture_header
0010da98 T h263_decode_init_vlc
001127c8 T h263_decode_picture_header
001ab040 D h263_decoder
00106c44 T h263_encode_gob_header
0010b2b0 T h263_encode_init
00109d40 T h263_encode_mb
00105f94 T h263_encode_picture_header
001a85a0 D h263_encoder
001162d0 T h263_get_picture_format
0010a7b4 T h263_pred_motion
00106df8 T h263_send_video_packet
001ab180 D h263i_decoder
001a85e0 D h263p_encoder
00115c68 T intel_h263_decode_picture_header
其中,T表示正常代码段,D表示初始化数据段

5.2库的构建与析构函数
  关于构建与析构函数,一般不需要自己去编程实现。如果你一定要自己做,下面是函数原型:
  void __attribute__ ((constructor)) my_init(void);
  void __attribute__ ((destructor)) my_fini(void);
  在编译共享库时,不能使用"-nonstartfiles"或"-nostdlib"选项,否则,构建与析构函数将不能正常执行(除非你采取一定措施)。

5.3脚本共享库
  linux中,共享库可以是脚本形式,当然需要专门的脚本语言。/usr/lib/libc.so是一个典型的例子,内容如下:
  /* GNU ld script
     Use the shared library, but some functions are only in
     the static library, so try that secondarily.  */
  OUTPUT_FORMAT(elf32-i386)
  GROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a )

5.4 版本脚本(略)
5.5 GNU libtool(略)

5.6除去记号信息
  共享库中的记号信息多为调试之用,但占用了磁盘空间。如果你的库是为嵌入式系统所用,最好去掉记号信息。一种方法,利用strip(1)命令,使用方法查看其帮助文档;另一种方法,使用GNU LD选项-s或-S,例如"-Wl -s"或"-Wl -S"。-S仅除去调试记号信息;-s除去所有记号信息。

5.7编译优化
  有一篇文章写的不错"Whirlwind tutorial On Creating really teensy ELF Executables For Linux"。这篇文章中可以说把程序的代码优化到了极点。在我们实际的应用中,可能并需要那些技巧,但通过此文,我们可以更多的了解ELF。

5.8 C++与C
  要使得你编写的共享库能同时被C和C++程序使用,库的头文件需要使用"extern C"预定义,下面是一个例子:
  #ifndef LIB_HELLO_H
  #define LIB_HELLO_H

  #ifdef __cplusplus
  extern "C"
  {
  #endif


  .....头文件代码
 
  #ifdef __cplusplus
  }
  #endif

  #endif

5.9关于C++程序的启动速度
  C++应用程序的启动速度是比较慢的。我一直使用firefox,感受颇深。有人认为这是因主函数启动之前的代码重定位所导致。有一篇文章"making C++ ready for the desktop"(by Waldo Bastian)对这问题作了分析。我读了一下,理解不是很深刻。

5.10 Linux Standard Base(LSB)
  LSB是一个项目,致力于制订和推动一系列标准,尽力提高不同Linux发布版本之间的兼容性,从而为应用程序的开发提供一致性的接口。关于linux标准项目的详细信息,可查阅网站。

 原文地址 http://wangbangjie.blogcn.com/diary,102307484.shtml
阅读(1374) | 评论(0) | 转发(0) |
0

上一篇:网卡工作原理[转载]

下一篇:天佑中华

给主人留下些什么吧!~~