花了我快半天的时间,居然是因为链接库版本不对

来源:互联网 发布:电力调度数据网骨干网 编辑:程序博客网 时间:2024/04/28 19:22

昨天本来要做一个摘要库的拆分,前两天新摘要库就开始建了,把摘要程序copy过来,运行,看log,居然出线socket err!吓了我一跳,这个错误应该是不会出现的,再仔细把改的脚本看了一遍,确定无误。我猜想是不是摘要程序的问题,于是,我从svn上更新了摘要程序源码,编译,运行。

       这下可真的是傻眼了,程序core了!!!svn的代码居然错了!!!

       gdb一看,居然是opendb error,我的bdb库打开错误。真的是惊奇,诧异……原以为是svn的代码哪里改了,check了一下确认改动的地方和这个无关,于是又找到原来的源码编译了一下,也是core。无语~~!

       最后,比较了源码还有静态库无果的情况下,我下意识的ldd了一下两个程序,哈~~,bdb的动态库链接居然不一样:      一个是libdb-4.1.so,一个是libdb-4.2.so

       真相大白了,我这台机子的bdb的版本是4.2的,居然和原来的4.1不兼容,而我用的bdb的包装静态库,居然只支持4.1 !真不知道那个bdb的静态库是怎么写的。

       Ok,要解决问题了,由于linux下的动态库都是有版本控制的,新的版本的被注册到ldconfig里面,这样编译的时候,链接的都是最新的库。我试着将链接bdb的路径指到/lib(Makefile里用L/lib指定ldb的路径),发现在这个路径下也有4.2的动态库。后来看了看网上的资料,原来直接写绝对路径编译就可以了。嗯,问题解决了,也耗了我好多时间,不过凡事都有第一次,下回的有经验了,呵呵~~。

 

 

       Ps:附上我找到的关于linux下动态库方面的文章。

 

 

 

[以下内容整理自《创建和使用库》一文,作者不详]

 

  C语言中有一些函数不需要进行编译,也可以在多个文件中使用。

  一般来说,有此函数会执行一定的标准任务,如数据库输入/输出操作或

屏幕控制等。可以事先对这些函数进行编译,然后将它们放置在一些特殊的目

标代码文件中,这些目标代码文件就称为库。

  库文件中的函数可以通过连接程序与应用程序进行连接。这样就不必在每

次开发程序时都对这些通用的函数进行编译了。

 

  不同类型的应用程序会使用不同的函数库。例如:libdbm库中组包含了对

数据库文件进行访问的dbm函数,需要对数据库进行操作的程序就会与该库进

行连接。数学应用程序使用数学库libmX-Windows应用程序使用Xlib库,

libX11。另外,所有的程序都将使用标准的C函数库。libc,该库中包含了内

存管理或输入输出操作的基本函数,这些库都存放在/usr/lib这些系统公用

的目录中,系统中的任何用户都可以利用这些库。当然用户也可以建立自己

专用的库函数,供自己或其它指定的人员使用。

 

 

  库可以有三种使用的形式:静态、共享和动态。静态库的代码在编译时就

已连接到开发人员开发的应用程序中,而共享库只是在程序开始运行时才载入,

在编译时,只是简单地指定需要使用的库函数。动态库则是共享库的另一种变

化形式。动态库也是在程序运行时载入,但与共享库不同的是,使用的库函数

不是在程序运行开始,而是在程序中的语句需要使用该函数时才载入。动态库

可以在程序运行期间释放动态库所占用的内存,腾出空间供其它程序使用。由

于共享库和动态库并没有在程序中包括库函数的内容,只是包含了对库函数的

引用,因此代码的规模比较小。

 

  已经开发的大多数库都采取共享库的方式。ELF格式的可执行文件使得共享

库能够比较容易地实现,当然使用旧的a.out模式也可以实现库的共享。Linux

系统中目前可执行文件的标准格式为ELF格式。

 

  GNU库的使用必须遵守Library GNU Public License(LGPL许可协议)。该

协议与GNU许可协议略有不同,开发人员可以免费使用GNU库进行软件开发,但

必须保证向用户提供所用的库函数的源代码。

 

  系统中可用的库都存放在/usr/lib/lib目录中。库文件名由前缀lib

库名以及后缀组成。根据库的类型不同,后缀名也不一样。共享库的后缀名

.so和版本号组成,静态库的后缀名为.a。采用旧的a.out格式的共享库的

后缀名为.sa

 

  使用gcc编译器就可以将库与自己开发的程序连接起来,例如:libc.so.5

中包含了标准的输入输出函数,当连接程序进行目标代码连接时会自动搜索该

程序并将其连接到生成的可执行文件中。标准的输入输出库中包含了许多基本

的输入输出函数,如printf函数等。也可以连接其它的一些系统函数库,如数

学库等,但与libc.so.5不同,大部分其它的系统库需要在命令行中显式指定

所用的库名。

 

    /usr/lib/lib目录中可以找到绝大多数的共享库。连接时将首先搜索

这两个目录。有一些库也可能存放在特定的目录中,在/etc/ld.conf配置文件

中给出了这些目录的列表。连接程序也会对列出的这些目录进行搜索。在默认

情况下,Linux将首先搜索指定库的共享版本,如果找不到,才会去搜索静态

版本。在对共享库进行更新或安装新库后,必须运行ldconfig命令更新

/etc/ld.conf文件中相应的项(如果使用RPM进行安装,一般会自动进行更新,

不过也不能保证这一点)

 

    gcc编译器中引用可搜索到的目录中的库文件时,需要使用-l选项和库

名。在gcc命令行上输入-lm可以在程序中连接标准算术库,-l将首先使用

libname.so进行搜索,这里是libm.so。下面的例子将使用算术库创建bookrecs

程序,请注意这里的-lm选项。

$ gcc main.c io.c -o bookrecs -lm

 

    系统中还有一些其它可用的库,常用的是libncurses.a库,包含了一些简

单的鼠标移动例程。在命令行中使用-lncurses选项引用libncurses.so库。下

面的例子同时调用了数学和光标库。

$ gcc mian.c io.c -o bookrecs -lm -lncurses

 

    在引用其它目录中的库时,需要使用-ldir选项指定该目录。该选项指定

了搜索库函数时其它路径。在下面的例子中,用户在连接时使用了mydir目录

中的myio.so库文件。

$ gcc main.c -o bookrecs -lmydir -lmyio

 

  Linux下文件的类型是不依赖于后缀名的,但一般来讲:

  .o  为目标文件,类似于windows中的.obj文件

   .so 为共享库文件,用于动态连接,类似于dll文件

  .a  为静态库,是多个.o文件合在一起,用于静态连接

   .la libtool自动生成的一些共享库,可用vi编辑查看,主要记录了一

些配置信息。

   

四、创建和使用Linux库文件

 

  [以下内容整理自《Linux静态/动态链接库的创建和使用》一文,

  http://blog.csdn.net/hcj2002/]

 

  Windows系统一样Linux也有静态/动态链接库,下面介绍创建和使用

方法:

 

  假设有下面几个文件:

  头文件String.h,声明相关函数原形,内容如下:

  Strlen.c:函数Strlen的实现,获取给定字符串的长度,内容如下:

  Strlnen.c:函数StrNlen的实现,获取给定字符串的长度,如果输入字

符串的长度大于指定的最大长度,则返回最大长度,否者返回字符串的实际

长度,内容如下:

  生成静态库:

 

  利用GCC生成对应目标文件:

  gcc –c Strlen.c Strnlen.c

  如果对应的文件没有错误,gcc会对文件进行编译生成Strlen.oStrnlen.o

两个目标文件(相当于windows下的obj文件)。然后用ar创建一个名字为libstr.a

的库文件,并把Strlen.o Strnlen.o的内容插入到对应的库文件中。相关命

令如下:

  ar –rc libstr.a Strlen.o Strnlen.o

  命令执行成功以后,对应的静态库libstr.a已经成功生成。

 

  /***********************************

  Filename : String.h

  Description :

  Author   : HCJ

  Date     : 2006-5-7

  ************************************/

 

 

  int Strlen(char *pStr);

  int StrNlen(char *pStr, unsigned long ulMaxLen);

 

  

 

  /**************************************

  Filename    : get string length

  Description  :

  Author      : HCJ

  Date        : 2006/5/7

  **************************************/

  #include<stdio.h>

  #include<assert.h>

 

  int Strlen(char *pStr)

  {

      unsigned long ulLength;

      assert(NULL != pStr);

 

      ulLength = 0;

      while(*pStr++)

      {

          ulLength++;

      }

 

      return ulLength;

  }

 

 

  **********************************************

  Fileneme: mystrnlen.c

  Description: get input string length,if string large

               max length input return max length,

               else real length

  Author: HCJ

  Date  : 2006-5-7

  **********************************************/

 

  #include<stdio.h>

  #include<assert.h>

 

  int StrNlen(char *pStr, unsigned long ulMaxLen)

  {

      unsigned long ulLength;

 

      assert(NULL != pStr);

 

      if(ulMaxLen <= 0)

      {

          printf("Wrong Max Length!/n");

          return -1;

      }

 

      ulLength = 0;

      while(*pStr++ &&  ulLength < ulMaxLen)

      {

          ulLength++;

      }

 

      return ulLength;

  }

 

 

  生成动态链接库:

   gcc  -fpic -shared -o libstr.so  Strlen.c Strnlen.c

  -fpic 使输出的对象模块是按照可重定位地址方式生成的。

  -shared指定把对应的源文件生成对应的动态链接库文件libstr.so

文件。

 

  对应的链接库已经生成,下面看一下如何使用对应的链接库。

  静态库的使用:

  假设有下面的文件要使用对应的的静态库:

  编译生成对应的目标文件:

  gcc -c -I/home/hcj/xxxxxxxx main.c

  生成可执行文件:

  gcc -o main1 -L/home/hcj/xxxxxxxx main.o libstr.a

  其中-I/home/hcj/xxxxxxxx-L/home/hcj/xxxxxxxx是通过-I-L

定对应的头文件和库文件的路径。libstr.a是对应的静态库的名称。这样

对应的静态库已经编译到对应的可执行程序中。执行对应的可执行文件便

可以对应得函数调用的结果。

 

  /*****************************************

  FileName: main.c

  Description: test static/dynamic library

  Author: HCJ

  Date  : 2005-5-7

  ******************************************/

  #include<stdio.h>

  #include <String.h>   //静态库对应函数的头文件

 

  int main(int argc, char* argv[])

  {

      char str[] = {"hello world"};

      unsigned long ulLength = 0;

 

      printf("The string is : %s/n", str);

      ulLength = Strlen(str);

      printf("The string length is : %d(use Strlen)/n", ulLength);

      ulLength = StrNlen(str, 10);

      printf("The string length is : %d(use StrNlen)/n", ulLength);

 

      return 0;

  }

 

  动态库的分为隐式调用和显式调用两种调用方法:

  隐式调用的使用使用方法和静态库的调用差不多,具体方法如下:

  gcc -c -I/home/hcj/xxxxxxxx main.c

  gcc -o main1 -L/home/hcj/xxxxxxxx main.o libstr.so  //这里是*.so

 

  在这种调用方式中,需要维护动态链接库的配置文件/etc/ld.so.conf

来让动态链接库为系统所使用,通常将动态链接库所在目录名追加到动态链

接库配置文件中。否则在执行相关的可执行文件的时候就会出现载入动态链

接库失败的现象。在编译所引用的动态库时,可以在gcc采用 l-L选项或

直接引用所需的动态链接库方式进行编译。在Linux里面,可以采用ldd命令

来检查程序依赖共享库。

 

  显式调用:

 

  /*****************************************

  FileName: main2.c

  Description: test static/dynamic library

  Author: HCJ

  Date  : 2005-5-7

  ******************************************/

  #include<stdio.h>

  #include<dlfcn.h>

 

  int main(int argc, char* argv[])

  {

      //define function pointor

      int (*pStrlenFun)(char* pStr);     //声明对应的函数的函数指针

      int (*pStrnlenFun)(char* pStr, int ulMaxLen);

 

      char str[] = {"hello world"};

      unsigned long ulLength = 0;

 

      void *pdlHandle;

      char *pszErr;

 

      pdlHandle = dlopen("./libstr.so", RTLD_LAZY);  //加载链接库/libstr.so

      if(!pdlHandle)

      {

          printf("Failed load library/n");

      }

      pszErr = dlerror();

      if(pszErr != NULL)

      {

          printf("%s/n", pszErr);

          return 0;

      }

 

      //get function from lib

      pStrlenFun = dlsym(pdlHandle, "Strlen"); //获取函数的地址

      pszErr = dlerror();

      if(pszErr != NULL)

      {

          printf("%s/n", pszErr);

          return 0;

      }

 

      pStrnlenFun = dlsym(pdlHandle, "StrNlen");

      pszErr = dlerror();

      if(pszErr != NULL)

      {

          printf("%s/n", pszErr);

          return 0;

      }

 

      printf("The string is : %s/n", str);

      ulLength = pStrlenFun(str);   //调用相关的函数

      printf("The string length is : %d(use Strlen)/n", ulLength);

      ulLength = pStrnlenFun(str, 10);

      printf("The string length is : %d(use StrNlen)/n", ulLength);

   dlclose(pdlHandle);

      return 0;

  }

 

  gcc -o mian2 -ldl main2.c

  gcc编译对应的源文件生成可执行文件,-ldl选项,表示生成的对象模

块需要使用共享库。执行对应得文件同样可以得到正确的结果。

  相关函数的说明如下:

  (1)dlopen()

  第一个参数:指定共享库的名称,将会在下面位置查找指定的共享库。

  -环境变量LD_LIBRARY_PATH列出的用分号间隔的所有目录。

  -文件/etc/ld.so.cache中找到的库的列表,用ldconfig维护。

  -目录usr/lib

  -目录/lib

  -当前目录。

  第二个参数:指定如何打开共享库。

  RTLD_NOW:将共享库中的所有函数加载到内存

  RTLD_LAZY:会推后共享库中的函数的加载操作,直到调用dlsym()

方加载某函数

  (2)dlsym()

  调用dlsym时,利用dlopen()返回的共享库的phandle以及函数名称作为

参数,返回要加载函数的入口地址。

  (3)dlerror()

  该函数用于检查调用共享库的相关函数出现的错误。

  这样我们就用简单的例子说明了在Linux下静态/动态库的创建和使用

 

原创粉丝点击