构建Linux程序库和使用Linux程序库.doc_第1页
构建Linux程序库和使用Linux程序库.doc_第2页
构建Linux程序库和使用Linux程序库.doc_第3页
构建Linux程序库和使用Linux程序库.doc_第4页
构建Linux程序库和使用Linux程序库.doc_第5页
已阅读5页,还剩15页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

构建Linux程序库和使用Linux程序库【转载】构建Linux程序库和使用Linux程序库2008-09-10 15:59 在本文里,我们将探索与Linux的程序库有关的知识。首先,我们考察静态库的基本知识,并介绍如何使用ar命令来建立静态库。然后,我们将学习共享库方面的知识,并讲述可以动态加载的共享库的有关内容。一、什么是程序库通俗的讲,一个程序库就是目标程序文件的一个集合。如果某些目标文件提供了解决一个特定问题的所需功能,我们就可以把这些目标文件归并为一个程序库,从而让应用开发者更易于访问这些目标文件,省得到处去找。对于静态库,我们可以用实用程序ar来建立。当应用程序开发人员利用程序库进行程序的编译和连接时,程序库中为应用程序所需的那些元件就会集成到最终生成的的可执行程序中。之后,因为程序库已经融入应用程序的映像之中,成为它密不可分的一部分了,所以对应用程序来说,已经没什么外部的程序库可言了。共享程序库(或者动态程序库)也会连接到一个应用程序的映像上,不过需要两个不同的步骤。第一步发生在构建应用程序之时,链接程序检查是否在应用程序或者程序库内部找到了构建应用程序所需的全部符号(函数名或变量名)。第二步发生在运行时,动态加载器把所需的共享库载入内存,然后动态地把它链接到应用程序的映像之中。注意,这里与静态程序库不同,这次并没有把共享程序库中的所需元件放入应用程序的映像之中。很明显,这样生成的应用程序映像较小,因为共享程序库和应用程序的映像是相互独立的,如下图所示。图1 静态库示意图图2 动态库示意图虽然共享库能够节约内存,但是这是有代价的必须在运行时解析程序库。很明显,要想弄清需要哪些库,然后寻找这些库并将其载入内存肯定是需要一定时间的。本文中,我们会建立两个程序库,一个静态库和一个动态库,并以各自的方式应用于程序之中,以此亲身体验两者之间的区别。二、静态库的创建和使用相对于动态链接库,静态库要简单一些,它被静态的链接到应用程序的映像之中。这意味着,映像一旦建好,外部程序库的有无对映像的执行将毫无影响,因为所需的部分已经放进程序二进制映像了。下面我们来演示如何用一组源文件来构造一个程序库。我们建立的程序库是用来封装GNU/Linux的随机函数的,这样我们的库就可以对外提供随机数生成器了。现在看一下我们的程序库为应用程序提供的接口(API),我们将其放在头文件randapi.h中,如下所示:/randapi.h,我们的程序库的接口#ifndef _RAND_API_H#define _RAND_API_Hextern void initRand( void );extern float getSRand( void );extern int getRand( int max );#endif /* _RAND_API_H */ 我们的应用程序接口由三个函数构成,第一个函数是initrand(),这是一个初始化函数,它的任务是为使用程序库做好必要的准备,在调用所有其他随机函数之前,必须首先调用这个初始化函数。第二个函数getSRand()的作用是随机返回一个浮点数,其值介于0.0到1.0之间。最后一个函数是getRand(x),它返回一个随机整数,其值介于0到(x1)之间。在文件initrand.c中,放的是初始化函数initrand()的实现代码,这个函数使用当前时间作为种子值来初始化随机数生成程序。代码如下所示:/initrand.c,初始化函数initrand()的源代码#include #include /initRand()用于初始化随机数生成器void initRand()time_t seed;seed = time(NULL);srand( seed );return; 文件randapi.c是我们最后一个实现API的文件,它也提供了一个随机数函数,源代码如下所示:/randapi.c,随机数函数的API实现#include /getSRand()返回一个介于0.01.0之间的浮点数float getSRand()float randvalue;randvalue = (float)rand() / (float)RAND_MAX);return randvalue;/getRand()返回一个介于0(max-1)之间的整数int getRand( int max )int randvalue;randvalue = (int)(float)max * rand() / (RAND_MAX+1.0);return randvalue; 这就是我们的API了,注意,initapi.c和randapi.c的函数原型都放在了同一个头文件中,即randapi.h.当然,我们完全可以在单个文件中来实现这些功能,但是为了示范程序库的建立,我们故意将它们放到不同的文件中。现在,我们在来看一下使用这些API的测试程序,该程序将使用我们的程序库提供的应用编程接口进行工作。这个应用程序通过计算随机数函数返回的的平均值来快速检验API,因为平均值应该在随机数范围的中间附近。该程序的代码如下所示:/test.c,测试我们的程序库的API的应用程序。#include randapi.h#define ITERATIONS 1000000Lint main()long i;long isum;float fsum;/*初始化随机数API*/initRand();/*计算getRand(10)的返回值的平均数*/isum = 0L;for (i = 0 ; i ITERATIONS ; i+)isum += getRand(10);printf( getRand() Average %dn, (int)(isum / ITERATIONS) );/* 计算getSRand()返回值的平均数*/fsum = 0.0;for (i = 0 ; i not foundlibc.so.6 = /lib/tls/libc.so.6 (0x42000000)/lib/ld-linux.so.2 = /lib/ld-linux.so.2 (0x40000000)$ 命令ldd能确定出我们的测试程序需要用到哪些共享库。其中libc.so.6是标准C库,ld-linux.so.2是动态链接器/装载器。注意,这里显示没有发现libmyrand.so,这是因为虽然该文件存在于应用程序的目录中,但是我们必须显式指出。我们可以通过环境变量LD_LIBRARY_PATH来完成此任务。给出我们的共享库的位置之后,再次使用ldd命令:$ export LD_LIBRARY_PATH=./$ ldd testlibmyrand.so = ./libmyrand.so (0x40017000)libc.so.6 = /lib/tls/libc.so.6 (0x42000000)/lib/ld-linux.so.2 = /lib/ld-linux.so.2 (0x40000000)$ 我们显式说明了共享库位于当前目录(。/)之后,再次运行ldd命令,它现在终于找到所需共享库了。如果我们不这样做就急着执行我们的应用程序的话,将会收到一条错误消息,指出找不到所需的共享库,如下所示:$ ./test./test: error while loading shared libraries: libmyrand.so:cannot find shared object file: No such file or directory.$ 四、动态库的创建和使用我们最后要介绍的是一种动态加载的共享库,或称为动态链接库。它的特点是在应用程序运行过程中,什么时候需要了才装入内存,而不是像共享库那样当应用程序启动时就把程序库装入内存。为此,我们还要像前面那样来构建共享的目标文件,如下所示:$ gcc -fPIC -c initapi.c$ gcc -fPIC -c randapi.c$ gcc -shared initapi.o randapi.o -o libmyrand.so$ su -$ cp libmyrand.so /usr/local/lib$ exit 在这里,我们将我们的共享库放到一个公共位置,即/usr/local/lib目录。就像我们所看到的那样,静态库和共享库通常跟应用程序的二进制文件放置在同一目录下,与之不同,动态链接库则一般放在/usr/local/lib目录中。这个库跟原来的共享库在功能上是等价的,只是应用程序使用它们的方式有所区别。需要说明的是,为了把我们的程序库复制到/usr/local/lib中,需要root权限。为此,可以首先使用su命令变成root用户。现在,我们已经重建了自己的共享库,接下来就是我们的测试程序如何动态使用它了。为此,我们必须对测试程序访问API的方式做一些修改。然后,我们再来看一下如何构建使用动态链接库的程序。更新后的测试程序代码如下所示:/用于动态链接库的测试程序#include #include #include #include randapi.h#define ITERATIONS 1000000Lint main()long i;long isum;float fsum;void *handle;char *err;void (*initRand_d)(void);float (*getSRand_d)(void);int (*getRand_d)(int);/打开动态库的句柄handle = dlopen( /usr/local/lib/libmyrand.so, RTLD_LAZY );if (handle = (void *)0)fputs( dlerror(), stderr );exit(-1);/检查对initRand()函数的访问情况initRand_d = dlsym( handle, initRand );err = dlerror();if (err != NULL)fputs( err, stderr );exit(-1);/检测对getSRand()函数的访问情况getSRand_d = dlsym( handle, getSRand );err = dlerror();if (err != NULL)fputs( err, stderr );exit(-1);/检查getRand()函数的访问情况getRand_d = dlsym( handle, getRand );err = dlerror();if (err != NULL)fputs( err, stderr );exit(-1);/随机数API的初始化(*initRand_d)();/计算getRand(10)返回值的平均数isum = 0L;for (i = 0 ; i ITERATIONS ; i+)isum += (*getRand_d)(10);printf( getRand() Average %dn, (int)(isum / ITERATIONS) );/计算getSRand()函数返回值的平均数fsum = 0.0;for (i = 0 ; i ITERATIONS ; i+)fsum += (*getSRand_d)();printf( getSRand() Average %fn, (fsum / (float)ITERATIONS) );/关闭动态库的句柄dlclose( handle );return; 与之前的代码相比,您可能觉得这里的有些费解,但是只要弄懂了DL API的工作方式,一切问题就迎刃而解了。我们这里的真实意图是,先使用dlopen打开共享目标文件,然后通过dlsym让一个局部函数指针指向共享目标文件中的函数。这使得我们可以之后从应用程序中调用该函数。做完这些后,使用dlclose关闭共享库,清除所有引用,即释放为该接口分配的所有内存。为了能够使用这些DL API,我们需要在应用程序中包含头文件dlfcn.h.使用动态库的第一步是用dlopen来打开它,其中代码为:handle = dlopen( /usr/local/lib/libmyrand.so, RTLD_LAZY ); 我们不仅要规定要使用的程序库(本例为/usr/local/lib/libmyrand.so),除此之外还得规定一个标志。我们这里可能用到的标志有两个,RTLD_LAZY和RTLD_NOW,如果规定RTLD_LAZY,那么就会在将来用到时才解析引用;如果规定RTLD_NOW标志的话,就会在装入程序库时就解析各种引用。如果函数dlopen返回的是一个opaque句柄,说明程序库已经打开了。需要注意的是,如果出现错误的话,我们可以使用dlerror函数向stdout或者stderr输出错误信息。此外,如果必要的话,我们可以在共享库中创建一个名为_init的函数,这样通过dlopen打开我们的共享库时,就会调用该函数,以进行初始化。因为dlopen函数总是在返回到调用者之前调用这个_init函数。下面,让我们看看是如何引用库中的函数的,或者说如何通过函数名来调用库函数的。先看一下下面的代码:initRand_d = dlsym( handle, initRand );err = dlerror();if (err != NULL)fputs( err, stderr );exit(-1); 由此代码片断可以看出,该过程是很简单的。API函数dlsym会在我们的共享库中搜索定义的函数,就本例而言要找的是初始化函数initrand.如果找到了,会返回一个void *指针,并将其放进一个局部函数指针变量中,这样就可以调用这个函数来进行实际的初始化工作了。我们自动地检验错误状态,如果返回错误字符串,那么会发出该字符串并退出应用程序。这就是发现我们想要调用的共享库函数的过程。获取initrand函数指针之后,我们又得到getSRand以及getRand的函数指针。我们的测试程序基本没变,只是没有直接调用函数,而是使用指针到函数的接口来间接调用它们。我们看到,虽然动态载入的接口在灵活性方面有所提高,但是在性能上却稍微有所损失。在新测试程序中,最后一步是关闭程序库,这是通过API函数dlclose来进行的。如果该API发现没有其他用户再使用该共享库的话,它

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论