linux系统中 库分为静态库和,Linux系统中“动态库”和“静态库”那点事儿-【经典好文】...

今天我们主要来说说Linux系统下基于动态库(.so)和静态(.a)的程序那些猫腻。在这之前,我们需要了解一下源代码到可执行程序之间到底发生了什么神奇而美妙的事情。

在linux操作系统中,普遍使用ELF格式作为可执行程序或者程序生成过程中的中间格式。ELF(Executable and Linking Format,可执行连接格式)是UNIX系统实验室(USL)作为应用程序二进制接口(Application BinaryInterface,ABI)而开发和发布的。工具接口标准委员会(TIS)选择了正在发展中的ELF标准作为工作在32位Intel体系上不同操作系统之间可移植的二进制文件格式。本文不对ELF文件格式及其组成做太多解释,以免冲淡本文的主题,大家只要知道这么个概念就行。以后再详解Linux中的ELF格式。源代码到可执行程序的转换时需要经历如下图所示的过程:

0_1331537030mwmn.gif

l 编译是指把用高级语言编写的程序转换成相应处理器的汇编语言程序的过程。从本质上讲,编译是一个文本转换的过程。对嵌入式系统而言,一般要把用C语言编写的程序转换成处理器的汇编代码。编译过程包含了c语言的语法解析和汇编码的生成两个步骤。编译一般是逐个文件进行的,对于每一个C语言编写的文件,可能还需要进行预处理。

l 汇编是从汇编语言程序生成目标系统的二进制代码(机器代码)的过程。机器代码的生成和处理器有密切的联系。相对于编译过程的语法解析,汇编的过程相对简单。这是因为对于一款特定的处理器,其汇编语言和二进制的机器代码是一一对应的。汇编过程的输入是汇编代码,这个汇编代码可能来源于编译过程的输出,也可以是直接用汇编语言书写的程序。

l 连接是指将汇编生成的多段机器代码组合成一个可执行程序。一般来说,通过编译和汇编过程,每一个源文件将生成一个目标文件。连接器的作用就是将这些目标文件组合起来,组合的过程包括了代码段、数据段等部分的合并,以及添加相应的文件头。

GCC是Linux下主要的程序生成工具,它除了编译器、汇编器、连接器外,还包括一些辅助工具。在下面的分析过程中我会教大家这些工具的基本使用方法,Linux的强大之处在于,对于不太懂的命令或函数,有一个很强大的“男人”时刻stand by your side,有什么不会的就去命令行终端输入:man [命令名或函数名],然后阿拉神灯就会显灵了。

对于最后编译出来的可执行程序,当我们执行它的时候,操作系统又是如何反应的呢?我们先从宏观上来个总体把握,如图2所示:

0_1331537040ALw9.gif

作为UNIX操作系统的一种,Linux的操作系统提供了一系列的接口,这些接口被称为系统调用(System Call)。在UNIX的理念中,系统调用"提供的是机制,而不是策略"。C语言的库函数通过调用系统调用来实现,库函数对上层提供了C语言库文件的接口。在应用程序层,通过调用C语言库函数和系统调用来实现功能。一般来说,应用程序大多使用C语言库函数实现其功能,较少使用系统调用。

那么最后的可执行文件到底是什么样子呢?前面已经说过,这里我们不深入分析ELF文件的格式,只是给出它的一个结构图和一些简单的说明,以方便大家理解。

ELF文件格式包括三种主要的类型:可执行文件、可重定向文件、共享库。

1.可执行文件(应用程序)

可执行文件包含了代码和数据,是可以直接运行的程序。

2.可重定向文件(*.o)

可重定向文件又称为目标文件,它包含了代码和数据(这些数据是和其他重定位文件和共享的object文件一起连接时使用的)。

*.o文件参与程序的连接(创建一个程序)和程序的执行(运行一个程序),它提供了一个方便有效的方法来用并行的视角看待文件的内容,这些*.o文件的活动可以反映出不同的需要。

Linux下,我们可以用gcc -c编译源文件时可将其编译成*.o格式。

3.共享文件(*.so)

也称为动态库文件,它包含了代码和数据(这些数据是在连接时候被连接器ld和运行时动态连接器使用的)。动态连接器可能称为ld.so.1,libc.so.1或者 ld-linux.so.1。我的CentOS6.0系统中该文件为:/lib/ld-2.12.so

0_13315370473OsQ.gif

一个ELF文件从连接器(Linker)的角度看,是一些节的集合;从程序加载器(Loader)的角度看,它是一些段(Segments)的集合。ELF格式的程序和共享库具有相同的结构,只是段的集合和节的集合上有些不同。

那么到底什么是库呢?

库从本质上来说是一种可执行代码的二进制格式,可以被载入内存中执行。库分静态库和动态库两种。

静态库:这类库的名字一般是libxxx.a,xxx为库的名字。利用静态函数库编译成的文件比较大,因为整个函数库的所有数据都会被整合进目标代码中,他的优点就显而易见了,即编译后的执行程序不需要外部的函数库支持,因为所有使用的函数都已经被编译进去了。当然这也会成为他的缺点,因为如果静态函数库改变了,那么你的程序必须重新编译。

动态库:这类库的名字一般是libxxx.M.N.so,同样的xxx为库的名字,M是库的主版本号,N是库的副版本号。当然也可以不要版本号,但名字必须有。相对于静态函数库,动态函数库在编译的时候并没有被编译进目标代码中,你的程序执行到相关函数时才调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。由于函数库没有被整合进你的程序,而是程序运行时动态的申请并调用,所以程序的运行环境中必须提供相应的库。动态函数库的改变并不影响你的程序,所以动态函数库的升级比较方便。linux系统有几个重要的目录存放相应的函数库,如/lib /usr/lib。

当要使用静态的程序库时,连接器会找出程序所需的函数,然后将它们拷贝到执行文件,由于这种拷贝是完整的,所以一旦连接成功,静态程序库也就不再需要了。然而,对动态库而言,就不是这样。动态库会在执行程序内留下一个标记指明当程序执行时,首先必须载入这个库。由于动态库节省空间,linux下进行连接的缺省操作是首先连接动态库,也就是说,如果同时存在静态和动态库,不特别指定的话,将与动态库相连接。

OK,有了这些知识,接下来大家就可以弄明白我所做的事情是干什么了。都说例子是最好老师,我们就从例子入手。

1、静态链接库

我们先制作自己的静态链接库,然后再使用它。制作静态链接库的过程中要用到gcc和ar命令。

准备两个库的源码文件st1.c和st2.c,用它们来制作库libmytest.a,如下:

0_1331537129vFvv.gif

静态库文件libmytest.a已经生成,用file命令查看其属性,发现它确实是归档压缩文件。用ar -t libmytest.a可以查看一个静态库包含了那些obj文件:

0_1331537139q26s.gif

接下来我们就写个测试程序来调用库libmytest.a中所提供的两个接口print1()和print2()。

0_1331537148mLvA.gif

看到没,静态库的编写和调用就这么简单,学会了吧。这里gcc的参数-L是告诉编译器库文件的路径是当前目录,-l是告诉编译器要使用的库的名字叫mytest。

2、动态库

静态库*.a文件的存在主要是为了支持较老的a.out格式的可执行文件而存在的。目前用的最多的要数动态库了。

动态库的后缀为*.so。在Linux发行版中大多数的动态库基本都位于/usr/lib和/lib目录下。在开发和使用我们自己动态库之前,请容许我先落里罗嗦的跟大家唠叨唠叨Linux下和动态库相关的事儿吧。

有时候当我们的应用程序无法运行时,它会提示我们说它找不到什么样的库,或者哪个库的版本又不合它胃口了等等之类的话。那么应用程序它是怎么知道需要哪些库的呢?我们前面已几个学了个很棒的命令ldd,用就是用来查看一个文件到底依赖了那些so库文件。

Linux系统中动态链接库的配置文件一般在/etc/ld.so.conf文件内,它里面存放的内容是可以被Linux共享的动态联库所在的目录的名字。我的系统中,该文件的内容如下:

0_1331537159ntMG.gif

然后/etc/ld.so.conf.d/目录下存放了很多*.conf文件,如下:

0_1331537167i202.gif

其中每个conf文件代表了一种应用的库配置内容,以MySQL为例:

0_13315371781xP9.gif

如果您是和我一样装的CentOS6.0的系统,那么细心的读者可能会发现,在/etc目录下还存在一个名叫ld.so.cache的文件。从名字来看,我们知道它肯定是动态链接库的什么缓存文件。

对,您说的一点没错。为了使得动态链接库可以被系统使用,当我们修改了/etc/ld.so.conf或/etc/ld.so.conf.d/目录下的任何文件,或者往那些目录下拷贝了新的动态链接库文件时,都需要运行一个很重要的命令:ldconfig,该命令位于/sbin目录下,主要的用途就是负责搜索/lib和/usr/lib,以及配置文件/etc/ld.so.conf里所列的目录下搜索可用的动态链接库文件,然后创建处动态加载程序/lib/ld-linux.so.2所需要的连接和(默认)缓存文件/etc/ld.so.cache(此文件里保存着已经排好序的动态链接库名字列表)。

也就是说:当用户在某个目录下面创建或拷贝了一个动态链接库,若想使其被系统共享,可以执行一下"ldconfig目录名"这个命令。此命令的功能在于让ldconfig将指定目录下的动态链接库被系统共享起来,即:在缓存文件/etc/ld.so.cache中追加进指定目录下的共享库。请注意:如果此目录不在/lib,/usr/lib及/etc/ld.so.conf文件所列的目录里面,则再次单独运行ldconfig时,此目录下的动态链接库可能不被系统共享了。单独运行ldconfig时,它只会搜索/lib、/usr/lib以及在/etc/ld.so.conf文件里所列的目录,用它们来重建/etc/ld.so.cache。

因此,等会儿我们自己开发的共享库就可以将其拷贝到/lib、/etc/lib目录里,又或者修改/etc/ld.so.conf文件将我们自己的库路径添加到该文件中,再执行ldconfig命令。

非了老半天功夫,终于把基础打好了,猴急的您早已按耐不住激情的想动手尝试了吧!哈哈。。。OK,说整咱就开整,接下来我就带领大家一步一步来开发自己的动态库,然后教大家怎么去使用它。

我们有一个头文件my_so_test.h和三个源文件test_a.c、test_b.c和test_c.c,将他们制作成一个名为libtest.so的动态链接库文件:

0_1331537188au0x.gif

OK,万事俱备,只欠东风。如何将这些文件编译成一个我们所需要的so文件呢?可以分两步来完成,也可以一步到位:

方法一:

1、先生成目标.o文件:

0_1331537195Ejhj.gif

2、再生成so文件:

0_13315372030wU1.gif

-shared该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件。

-fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。

方法二:一步到位。

0_1331540214c18A.gif

至此,我们制作的动态库文件libtest.so就算大功告成了。

接下来,就是如何使用这个动态库了。动态链接库的使用有两种方法:既可以在运行时对其进行动态链接,又可以动态加载在程序中是用它们。接下来,我就这两种方法分别对其介绍。

+++动态库的使用+++

用法一:动态链接。

0_1331537210T1RI.gif

使用“-ltest”标记来告诉GCC驱动程序在连接阶段引用共享函数库libtest.so。“-L.”标记告诉GCC函数库可能位于当前目录。否则GNU连接器会查找标准系统函数目录。

这里我们注意,ldd的输出它说我们的libtest.so它没找到。还记得我在前面动态链接库一节刚开始时的那堆唠叨么,现在你应该很明白了为什么了吧。因为我们的libtest.so既不在/etc/ld.so.cache里,又不在/lib、/usr/lib或/etc/ld.so.conf所指定的任何一个目录中。怎么办?还用我告诉你?管你用啥办法,反正我用的ldconfig `pwd`搞定的:

0_1331537220Jd4w.gif

执行结果如下:

0_133153722711o3.gif

偶忍不住又要罗嗦一句了,相信俺,我的唠叨对大家是有好处。我为什么用这种方法呢?因为我是在给大家演示动态库的用法,完了之后我就把libtest.so给删了,然后再重构ld.so.cache,对我的系统不会任何影响。倘若我是开发一款软件,或者给自己的系统DIY一个非常有用的功能模块,那么我更倾向于将libtest.so拷贝到/lib、/usr/lib目录下,或者我还有可能在/usr/local/lib/目录下新建一文件夹xxx,将so库拷贝到那儿去,并在/etc/ld.so.conf.d/目录下新建一文件mytest.conf,内容只有一行“/usr/local/lib/xxx/libtest.so”,再执行ldconfig。如果你之前还是不明白怎么解决那个“not found”的问题,那么现在总该明白了吧。

方法二:动态加载。

动态加载是非常灵活的,它依赖于一套Linux提供的标准API来完成。在源程序里,你可以很自如的运用API来加载、使用、释放so库资源。以下函数在代码中使用需要包含头文件:dlfcn.h函数原型说明

const char *dlerror(void)当动态链接库操作函数执行失败时,dlerror可以返回出错信息,返回值为NULL时表示操作函数执行成功。

void *dlopen(const char *filename, int flag)用于打开指定名字(filename)的动态链接库,并返回操作句柄。调用失败时,将返回NULL值,否则返回的是操作句柄。

void *dlsym(void *handle, char *symbol)根据动态链接库操作句柄(handle)与符号(symbol),返回符号对应的函数的执行代码地址。由此地址,可以带参数执行相应的函数。

int dlclose (void *handle)用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。2.2在程序中使用动态链接库函数。

dlsym(void *handle, char *symbol)

filename:如果名字不以“/”开头,则非绝对路径名,将按下列先后顺序查找该文件。

(1)用户环境变量中的LD_LIBRARY值;

(2)动态链接缓冲文件/etc/ld.so.cache

(3)目录/lib,/usr/lib

flag表示在什么时候解决未定义的符号(调用)。取值有两个:

1) RTLD_LAZY : 表明在动态链接库的函数代码执行时解决。

2) RTLD_NOW :表明在dlopen返回前就解决所有未定义的符号,一旦未解决,dlopen将返回错误。

dlsym(void *handle, char *symbol)

dlsym()的用法一般如下:

void(*add)(int x,int y); /*说明一下要调用的动态函数add */

add=dlsym("xxx.so","add"); /* 打开xxx.so共享库,取add函数地址 */

add(89,369); /* 带两个参数89和369调用add函数 */

看我出招:

0_1331537235Y6Ia.gif

执行结果:

0_1331537241hhBr.gif

使用动态链接库,源程序中要包含dlfcn.h头文件,写程序时注意dlopen等函数的正确调用,编译时要采用-rdynamic选项与-ldl选项(不然编译无法通过),以产生可调用动态链接库的执行代码。

OK,通过本文的指导、练习相信各位应该对Linux的库机制有了些许了解,最主要的是会开发使用库文件了。由于本人知识所限,文中某些观点如果不到位或理解有误的地方还请各位个人不吝赐教。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/434528.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux上的壁纸软件下载,Ubuntu 17.10上安装开源壁纸工具Wallch 4.0

在Ubuntu 17.04上安装WallchWallch是一款免费的开源通用壁纸更换器,用户可以在设置的时间更改桌面背景图片。它会在桌面上更改并显示随机图片或维基百科的当天图片。它还具有一个实时地球Living Earth壁纸,每30分钟更换桌面背景,以显示阳光和…

linux find显示文件的基本信息,命令find搜索文件,命令stat查看文件的详细信息

命令stat命令语法stat(选项)(参数)命令描述命令stat用于显示文件的状态信息。stat命令的输出信息比ls命令的输出信息要更详细。命令选项-L:支持符号连接;-f:显示文件系统状态而非文件状态;-t:以简洁方式输出信息&#…

Linux rpm 命令参数使用详解[介绍和应用]

RPM是RedHat Package Manager(RedHat软件包管理工具)类似Windows里面的“添加/删除程序” rpm 执行安装包 二进制包(Binary)以及源代码包(Source)两种。二进制包可以直接安装在计算机中,而源代…

Ubuntu10.10的网络配置

有一阵子着实对Ubuntu的网络配置很迷惑,耐下心来仔细上网找了找,有点小心得,总结一下。 先说下大概的配置过程,再去细究一些情况。 一、配置大概分三类:通过配置文件配置、通过命令配置、通过图形化的网络连接菜单配置…

DllMain详解

1 DLL的进入/退出函数 1.1 DllMain简介 跟exe有个main或者WinMain入口函数一样,DLL也有一个入口函数,就是DllMain。以“DllMain”为关键字,来看看MSDN帮助文档怎么介绍这个函数的。 The DllMain function is an optional method of entr…

linux安装tensorflow教程,真正从零开始,TensorFlow详细安装入门图文教程!

在正式开始之前我想说:一定要注意窗口给出的提示(英文)。在实际操作中可能会碰到各种各样的问题,但常见的问题其实都可以根据它的报错信息找到原因,只要上网搜一搜相应的信息就能解决,甚至它自己就会给出解决的建议。如果你发现你…

引路蜂地图API:Gis.Navigation包定义

本包提供了路口到路口实时导航API,从地图服务器返回的路径信息含有文字和路径的地理坐标信息,类NavigationEngine根据路径和当前坐标实现实时导航。它内部含用三个工作线程: Location Monitor 实时监视当前位置坐标是否偏离路径,如…

有些垃圾网站转载都不会

有时会看到我的文章被转载,只要保留作者信息和原文链接,并且忠实于原文都是很欢迎的。这里的忠实原文应该是最基本的了吧,转载嘛,最简单的也就是拷贝粘贴吧,可发现有些垃圾网站,连拷贝粘贴都做不好&#xf…

Linux部署动态网页,linux构建动态WEB服务器安装篇

linux构建动态WEB服务器安装篇发布时间:2008-09-18 08:50:01 作者:佚名 我要评论基本配置安装web服务器:httpd-2.X.X先优化吧根据CPU设置变量#export CFLAGS”-O2 -marchpentium4 -pipe” && CXXFLAGS$CFLAGS“-march”选项表示为特定的cpu…

sip事务与对话

一个事务通常由一个请求开始,由一个响应码(a response code)结束。VIA头域中的branch参数用来标识一个事务。 对话可以是开始于一个INVITE事务,结束于一个BYE事务。一个对话由FROM,TO和CALL-ID头域的结合所…

px、dp和sp,这些单位有什么区别?

相信每个Android新手都会遇到这个问题,希望这篇帖子能让你不再纠结。 px: 即像素,1px代表屏幕上一个物理的像素点; px单位不被建议使用,因为同样100px的图片,在不同手机上显示的实际大小可能不同&#x…

linux 例行性工作,Linux例行性工作

Linux例行性工作Linux:例行性工作什么是例行性工作?其实对于例行性工作而言就是在当前环境下周期性进行的必须工作。在Linux下,有2种例行性工作种类:一种是突发性,做完以后就没有的那种at:at是个可以处理仅执行一次就结束调度的命…

linux 下搭建yolov3错误,Ubuntu20.04+cuda11.1+yolo3 目标检测 深度学习系统 真正从0搭建 包含各类可能遇到的错误...

本篇文章包含内容较多,请参照目录浏览,在每一部分结束或,有该部分可能遇到的问题0、开始本教程前请先备份电脑中的重要文件!!!!一、Ubuntu20.04 搭建更详细的内容请参照该博客:https://www.cnblogs.com/mas…

SystemVerilog例子---traffic light

SystemVerilog例子---traffic light module traffic_light(output logic green_light,yellow_light,red_light,input sensor,input [15:0] green_downcnt,input [15:0] yellow_downcnt,input clock,input resetN); parameter R_BIT 0,G_BIT 1,Y_BIT 2;enum logic [2:0] {RE…

报表中的Excel操作之Aspose.Cells(Excel模板)

本篇中将简单记录下Aspose.Cells这个强大的Excel操作组件。这个组件的强大之处,就不多说,对于我们的报表总是会有导出Excel的处理,如果你使用微软Excel的com组件,那么对于简单的操作还行,但是多余复杂的模板&#xff0…

在飞音G801上运行OpenWRT+Asterisk

上篇文章介绍了如何在华硕RT-N11建立小型的SIP Server,本篇文章介绍将Asterisk移植到更为强大的VOIP网关G801上。G801是北京飞音时代的无线VoIP网关产品,在韩国SKT等运营商中广泛采用。G801支持802.11n 2T2R的无线路由,1WAN 4LAN的以太网&am…

android rsa加密工具类,GitHub - Lerist/encrypt: Android 加密解密工具包。

Encrypt(加密工具)字符串,byte[],文件等对象的加密和解密工具集合,包含了多种加密方案。加密类型摘要相关方法简单加密换一种编码格式Base64Util单向加密只能加密,不能解密MD5Util、SHAUtil对称加密使用相同的秘钥加密和解密AESUt…

开源应用架构之asterisk

作者:Russell Bryant 翻译:jiazhengfeng Asterisk[1]是一款GPLv2协议下的开源电话应用平台。简单来说,Asterisk是一个服务器应用,能够完成发起电话呼叫、接受电话呼叫、对电话呼叫进行定制处理。 Asterisk这个项目是由Mark Spen…

android 5.1 壁纸路径,RTFSC – Android5.1 壁纸设置流程简析 – RustFisher

Android5.1 壁纸设置流程浅析Ubuntu14.04 Android5.1 Source Insight3这里只是简单分析一下5.1里是如何设置壁纸的;这个流程和4.4有一些不同。但基本都是找个地方存放壁纸文件,需要的时候读取,设置的时候更新这里只看设置的过程。权当参考。…

android电梯程序设计,课内资源 - 基于Android实现的电梯调度模拟

一、使用说明1.1 项目简介某一楼有20层,操作者可以通过使用5部电梯从起始楼层来到达指定的楼层。本项目通过实现电梯调度,来模拟实现操作系统的调度过程,并且学习Android环境下使用Java的多线程编程方法以及调度算法。1.2 项目要求操作者可以…