Linux系统中“动态库”和“静态库”那点事儿 /etc/ld.so.conf 动态库的后缀为*.so 静态库的后缀为 libxxx.a ldconfig 目录名 谈谈Linux下动态库查...

Linux系统中“动态库”和“静态库”那点事儿 /etc/ld.so.conf  动态库的后缀为*.so  静态库的后缀为 libxxx.a   ldconfig   目录名

转载自:http://blog.chinaunix.net/uid-23069658-id-3142046.html

 

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

Win9x和Win NT/2000/XP下的32位的可执行文件PE(Portable Executable:可移动的可执行文件)

l 编译是指把用高级语言编写的程序转换成相应处理器的汇编语言程序的过程。从本质上讲,编译是一个文本转换的过程。对嵌入式系统而言,一般要把用C语言编写的程序转换成处理器的汇编代码。编译过程包含了C语言的语法解析和汇编码的生成两个步骤。编译一般是逐个文件进行的,对于每一个C语言编写的文件,可能还需要进行预处理。
l 汇编是从汇编语言程序生成目标系统的二进制代码(机器代码)的过程。机器代码的生成和处理器有密切的联系。相对于编译过程的语法解析,汇编的过程相对简单。这是因为对于一款特定的处理器,其汇编语言和二进制的机器代码是一一对应的。汇编过程的输入是汇编代码,这个汇编代码可能来源于编译过程的输出,也可以是直接用汇编语言书写的程序。
l 连接是指将汇编生成的多段机器代码组合成一个可执行程序。一般来说,通过编译和汇编过程,每一个源文件将生成一个目标文件。连接器的作用就是将这些目标文件组合起来,组合的过程包括了代码段、数据段等部分的合并,以及添加相应的文件头。
GCC是Linux下主要的程序生成工具,它除了编译器、汇编器、连接器外,还包括一些辅助工具。在下面的分析过程中我会教大家这些工具的基本使用方法,Linux的强大之处在于,对于不太懂的命令或函数,有一个很强大的“男人”时刻stand by your side,有什么不会的就去命令行终端输入:man [命令名或函数名],然后阿拉神灯就会显灵了。
对于最后编译出来的可执行程序,当我们执行它的时候,操作系统又是如何反应的呢?我们先从宏观上来个总体把握,如图2所示:

作为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

一个ELF文件从连接器(Linker)的角度看,是一些节的集合;从程序加载器(Loader)的角度看,它是一些段(Segments)的集合。ELF格式的程序和共享库具有相同的结构,只是段的集合和节的集合上有些不同。
那么到底什么是库呢?
库从本质上来说是一种可执行代码的二进制格式,可以被载入内存中执行。库分静态库和动态库两种。
静态库:这类库的名字一般是libxxx.a,xxx为库的名字。利用静态函数库编译成的文件比较大,因为整个函数库的所有数据都会被整合进目标代码中,他的优点就显而易见了,即编译后的执行程序不需要外部的函数库支持,因为所有使用的函数都已经被编译进去了。当然这也会成为他的缺点,因为如果静态函数库改变了,那么你的程序必须重新编译。
动态库:这类库的名字一般是libxxx.M.N.so,同样的xxx为库的名字,M是库的主版本号,N是库的副版本号。当然也可以不要版本号,但名字必须有。相对于静态函数库,动态函数库在编译的时候并没有被编译进目标代码中,你的程序执行到相关函数时才调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。由于函数库没有被整合进你的程序,而是程序运行时动态的申请并调用,所以程序的运行环境中必须提供相应的库。动态函数库的改变并不影响你的程序,所以动态函数库的升级比较方便。linux系统有几个重要的目录存放相应的函数库,如/lib /usr/lib。
当要使用静态的程序库时,连接器会找出程序所需的函数,然后将它们拷贝到执行文件,由于这种拷贝是完整的,所以一旦连接成功,静态程序库也就不再需要了。然而,对动态库而言,就不是这样。动态库会在执行程序内留下一个标记指明当程序执行时,首先必须载入这个库。由于动态库节省空间,linux下进行连接的缺省操作是首先连接动态库,也就是说,如果同时存在静态和动态库,不特别指定的话,将与动态库相连接。

 

很多命令都依赖libc.so.6 动态连接器

ldd /bin/ls
linux-vdso.so.1 => (0x00007fff446d9000)
libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fd04a836000)
librt.so.1 => /lib64/librt.so.1 (0x00007fd04a62e000)
libcap.so.2 => /lib64/libcap.so.2 (0x00007fd04a429000)
libacl.so.1 => /lib64/libacl.so.1 (0x00007fd04a221000)
libc.so.6 => /lib64/libc.so.6 (0x00007fd049e8d000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007fd049c88000)
/lib64/ld-linux-x86-64.so.2 (0x00007fd04aa66000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fd049a6b000)
libattr.so.1 => /lib64/libattr.so.1 (0x00007fd049866000)


ldd /bin/echo
linux-vdso.so.1 => (0x00007fffc43ff000)
libc.so.6 => /lib64/libc.so.6 (0x00007ff982abe000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff982e63000)


ldd /bin/mv
linux-vdso.so.1 => (0x00007fffff7ff000)
libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fb609ce3000)
librt.so.1 => /lib64/librt.so.1 (0x00007fb609adb000)
libacl.so.1 => /lib64/libacl.so.1 (0x00007fb6098d2000)
libattr.so.1 => /lib64/libattr.so.1 (0x00007fb6096cd000)
libc.so.6 => /lib64/libc.so.6 (0x00007fb609339000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007fb609134000)
/lib64/ld-linux-x86-64.so.2 (0x00007fb609f13000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fb608f17000)


OK,有了这些知识,接下来大家就可以弄明白我所做的事情是干什么了。都说例子是最好老师,我们就从例子入手。
1、静态链接库
我们先制作自己的静态链接库,然后再使用它。制作静态链接库的过程中要用到gcc和ar命令。
准备两个库的源码文件st1.c和st2.c,用它们来制作库libmytest.a,如下:

file /usr/bin/pkg-config
/usr/bin/pkg-config: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, stripped

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

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

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


2、动态库
静态库*.a文件的存在主要是为了支持较老的a.out格式的可执行文件而存在的。目前用的最多的要数动态库了。
动态库的后缀为*.so。在Linux发行版中大多数的动态库基本都位于/usr/lib和/lib目录下。在开发和使用我们自己动态库之前,请容许我先落里罗嗦的跟大家唠叨唠叨Linux下和动态库相关的事儿吧。
有时候当我们的应用程序无法运行时,它会提示我们说它找不到什么样的库,或者哪个库的版本又不合它胃口了等等之类的话。那么应用程序它是怎么知道需要哪些库的呢?我们前面已几个学了个很棒的命令ldd,用就是用来查看一个文件到底依赖了那些so库文件。
Linux系统中动态链接库的配置文件一般在/etc/ld.so.conf文件内,它里面存放的内容是可以被Linux共享的动态联库所在的目录的名字。我的系统中,该文件的内容如下:

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

 

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

如果您是和我一样装的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的动态链接库文件:

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

2、再生成so文件:

-shared该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件。
-fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。
方法二:一步到位。


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

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

+++动态库的使用+++
用法一:动态链接。

使用“-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`搞定的:
执行结果如下:

偶忍不住又要罗嗦一句了,相信俺,我的唠叨对大家是有好处。我为什么用这种方法呢?因为我是在给大家演示动态库的用法,完了之后我就把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函数 */
看我出招:

执行结果:

使用动态链接库,源程序中要包含dlfcn.h头文件,写程序时注意dlopen等函数的正确调用,编译时要采用-rdynamic选项与-ldl选项(不然编译无法通过),以产生可调用动态链接库的执行代码。
OK,通过本文的指导、练习相信各位应该对Linux的库机制有了些许了解,最主要的是会开发使用库文件了。由于本人知识所限,文中某些观点如果不到位或理解有误的地方还请各位个人不吝赐教。

 


 

谈谈Linux下动态库查找路径的问题 ldconfig LD_LIBRARY_PATH PKG_CONFIG_PATH

转载自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=23069658&id=4028681


学习到了一个阶段之后,就需要不断的总结、沉淀、清零,然后才能继续“上路”。回想起自己当年刚接触Linux时,不管是用源码包编译程序,还是程序运行时出现的和动态库的各种恩恩怨怨,心里那真叫一个难受。那时候脑袋里曾经也犯过嘀咕,为啥Linux不弄成windows那样呢,装个软件那个麻烦不说,连运行软件都这么恼火呢?如果那样的话就不叫Linux了。借用小米公司CEO雷军一句话:小米,为发烧而生。我认为:Linux,为真理而在。特别是为那些喜欢折腾,热衷技术背后原理和实现细节的人们而生。

说到和动态库查找路径相关的问题,总体上可以分为两类:
第一类:通过源代码编译程序时出现的找不到某个依赖包的问题,而如果此时你恰好已经按照它的要求确确实实、千真万确、天地良心地把依赖库给装好了,它还给你耍混、犯二,有一股折腾不死人不偿命的劲儿,那让人真是气儿不打一处来,如果Linux此时有头有脸,你是不是特想抽它丫两大嘴巴;
第二类:就是在运行程序的时候,明明把那个程序需要的依赖包都已经安装的妥妥的了,可运行的时候人家就告诉你说“error while loading shared libraries: libxxx.so.y: cannot open shared object file: No such file or directory”,任凭你怎么折腾都没用。此时你要是心想“撤吧,哥们,Linux太TM欺负人了,不带这么玩儿的”,那你就大错特错了,只要你抱着“美好的事情的总会发生”和“办法永远比问题多”的信念坚持下去,你就一定会成功。话的意思有点自欺欺人,精神鸦片的味道在里面,但确实是这么个理儿。

上面两类问题最大的原因就是,你没弄白它们的机制和原理。你看到的只是现象,当年学马克思主义主义哲学原理的时候,老师怎么教导我们的?要透过现象看本质。如果你把上面两中应用的原理搞清了,那问题不就自然而然的迎刃而解了么。下面咱们就一一探讨一下这两个问题,以便对新进学习Linux的朋友们起一个的参考资料的作用。

问题1:通过源代码安装程序
通过源码包安装程序时,主要用到了“三大步”策略:configure、make和make install 。出问题最多的就是在configure阶段,很多初学者由于不知道configure的那么多参数该怎么用,所以往往为了省事,一句简单的“./configure”下去,百分之八九十都能成功,可问题往往就出在剩下的百分之十几上面了。这让我们又一次相信了,小概率事件的发生对事情的影响是多么的深远。在安装的configure阶段,为了检测安装安装环境是否满足,通常情况下都是通过一个叫做pkg-config的工具来检测它需要依赖的动态库是否存在,这个工具我们在上一篇博文已经认识过了。pkg-config通常情况都是位于/usr/bin目录下,是个可执行程序。在configure阶段,通常都会用pkg-config来判断所依赖的动态库是否存在。现在问题就是,这个工具是如何判断的呢?它的依据是什么?当这两个问题弄明白了,真相也就大白了。

一般当我们安装完某个程序后,如果它提供了动态库的功能,在源码中都会有一个或多个以pc结尾的文件,当执行完make install后这些pc文件拷贝到${prefix}/lib/pkgconfig这个目录里,这里的prefix就是我们在configure阶段时通过配置参数--prefix指定的,缺省情况这个值就是/usr/local,所以这些pc文件最终会被拷贝到/usr/local/lib/pkgconfig目录下。可能有人会问,这些pc文件有啥用呢?我们随便打开一个来瞅瞅:
[root@localhost ~]# cat /usr/local/lib/pkgconfig/librtmp.pc
prefix=/usr/local
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
incdir=${prefix}/include


Name: librtmp
Description: RTMP implementation
Version: v2.3
Requires: libssl,libcrypto
URL: http://rtmpdump.mplayerhq.hu
Libs: -L${libdir} -lrtmp -lz
Cflags: -I${incdir}
跟我们configure阶段相关的主要集中在Libs和Cflags两项上面,如果你此时再执行下面这两条命令,就全明白了:
[root@localhost ~]# pkg-config --cflags librtmp
-I/usr/local/include
[root@localhost ~]# pkg-config --libs librtmp
-L/usr/local/lib -lrtmp -lz -lssl -lcrypto
也就是说,pkg-config把我们以前需要在Makefile里指定编译和链接时所需要用到的参数从手工硬编码的模式变成了自动完成,节约了多少跨平台移植的兼容性问题,我们是不是得感谢人家十八辈儿祖宗。假如说,如果我们将要的编译的软件包依赖librtmp这个动态库,那么此时在我系统上这个检测就算通过了。当然这只是第一步,检测过了不一定兼容,这里我们只讨论能不能找到依赖库的问题,兼容性问题那都不是个事儿,人家要啥版本你好生伺候就是了,这个没得商量,最好也不要商量,童叟无欺,不然后果很严重。好了,如果说找不到某个库该怎么办。前提是你确确实实已经安装了它需要的库,不用多想,原因只有一个,pkg-config找不到这个与这个库对应的pc文件。为什么会找不到呢,原因又有两点:
1、pkg-config搜索了所有它认为合适的目录都没找着这个库对应的pc文件的下落;
2、这个库在发布时根本就没有提供它的pc文件。
这里,我们严重“抗议、鄙视+抵制”第二种情况的软件包,而且也尽量不要它,一个出来混都不自报家门的家伙,肯定也好不到哪里去。那么,现在问题就只剩下一个了:pkg-config的查找路径是哪里?

pkg-config较老的版本里,缺省情况下会到/usr/lib/pkgconfig、/usr/loca/lib/pkgconfig、/usr/share/pkgconfig等目录下去搜索pc文件,据我所知在0.23以及之后的版本里pkg-config的源码里已经没有关于缺省搜索路径的任何硬编码的成分了,至于具体从哪个版本开始我也没有去追究,还望有知道的朋友分享一下。取而代之的是,当你看pkg-config的man手册时会有下面一段话:

pkg-config retrieves information about packages from special metadata files. These files are named after the package, with the extension .pc.
By default, pkg-config looks in the directory prefix/lib/pkgconfig for these files; it will also look in the colon-separated (on Windows, semicolon-separated) list of directories specified by the PKG_CONFIG_PATH environment variable.

以及这点补充:
PKG_CONFIG_PATH
A colon-separated (on Windows, semicolon-separated) list of directories to search for .pc files. The default directory will always be searched after searching the path; the default is libdir/pkg-config:datadir/pkgconfig where libdir is the libdir where pkg-config and datadir is the datadir where pkg-config was installed.


上面提到的prefix、libdir和datadir,就是安装pkg-config时被设定好的,具体情况是:
1、如果你是通过yum和rpm包安装的
prefix=/usr
libdir=${prefix}/lib=/usr/lib
datadir=${prefix}/share=/usr/share
2、如果你是通过源码包安装的,且没有指定prefix的值(指定的情况同1)
prefix=/usr/local
libdir=${prefix}/lib=/usr/local/lib
datadir=${prefix}/share=/usr/local/share

pkg-config在查找对应软件包的信息时的缺省搜索路径已经很清楚了,有一点写错了,不是${libdir}/pkg-config,而应该是${libdir}/pkgconfig和${datadir}/pkgconfig。如果你软件包对应的pc文件都不在这两个目录下时,pkg-config肯定找不到。既然原因都已经找到了,那解决办法也就多种多样了。
方案一:我们可以在安装我们那个被依赖的软件包时,在configure阶段用--prefix参数把安装目录指定到/usr目录下;
方案二:也可以按照上面说的,通过一个名叫PKG_CONFIG_PATH的环境变量来向pkg-config指明我们自己的pc文件所在的路径,不过要注意的是PKG_CONFIG_PATH所指定的路径优先级比较高,pkg-config会先进行搜索,完了之后才是去搜索缺省路径。
前者的优点是以后再通过源码安装软件时少了不少麻烦,缺点是用户自己的软件包和系统软件混到一起不方便管理,所以实际使用中,后者用的要多一些。

方案二在实际操作中有两种实现方式:
1、针对没有root权限的情况,大多数情况都是执行:
export PKG_CONFIG_PATH=/your/local/path:$PKG_CONFIG_PATH

然后,在configure时就绝对没问题了。
2、在用户的家目录下的.bash_profile文件里或系统文件/etc/profile的末尾添加上面一行也成,都可以。
至此,动态库查找问题的第一种情况就彻底解决了。想了解pc文件的更多细节的,可以参考http://people.freedesktop.org/~dbn/pkg-config-guide.html ; 想学习pkg-config工具更多用法的朋友建议看man手册。

问题2:程序运行时出现libxxx.so.y => not found
这种情况,在我以前的博文“Linux系统下动态库和静态库那点事儿”里已经提到一部分,这里就把它补充完整。在那篇博文里,我用的配置文件或者“ldconfig 动态库所在路径”的方式解决的,也是99%的场合下的解决办法,那是针对有root权限的用户的解决办法。没有root权限运行软件时,Linux也为我们提供了一个名为LD_LIBRARY_PATH的环境变量来解决运行时动态库查找路径的解决方案。同样地,由这个环境变量所指定的路径会被装载器/lib/ld-2.12.so优先查找,然后才是动态库缓存文件/etc/ld.so.cache,风采瞬间就被LD_LIBRARY_PATH给抢完了,/etc/ld.so.cache表示很不高兴。针对LD_LIBRARY_PATH环境变量这种情况,绝对是临时不能再临时解决方案了,如果只是测试用,用export像解决PKG_CONFIG_PATH一样的方式干净利索就行了,千万不要在实际生产上线的运维环境里把“export LD_LIBRARY_PATH=...” 添加到.bash_profile或者/etc/profile里,不然到时候悔得你肠子都绿了不可。

其实PKG_CONFIG_PATH和LD_LIBRARY_PATH经常被很多人误用,特别是新手们在解决问题时,也不分青红皂白,逮着了就是一顿狂export,根据实际场合,运气好了说不定问题还真就解决,点儿背了折腾一天半宿也是白忙活。其实要是留点心,还是挺容易明白的:
PKG_CONFIG_PATH从字面意思上翻译,就是“软件包的配置路径”,这不很明显了么,编译软件时如果出现找不到所依赖的动态库时都全靠PKG_CONFIG_PATH了;
LD_LIBRARY_PATH也很直白了“装载器的库路径”,LD是Loader的简写,在博文“段错误到底是何方妖孽”里我也曾提到过,在Linux系统启动一个程序的过程就叫做装载,一个程序要执行时它或多或少的会依赖一些动态库(静态编译的除外)。当你用“ldd 可执行程序名”查看一个软件启动时所依赖的动态库,如果输出项有“libxxx.so.y=> not found”一项,你这个软件100%运行不起来。

不信我们来做个试验:
[root@localhost ~]# echo $LD_LIBRARY_PATH //嘛也没有

[root@localhost ~]# ldd /usr/local/bin/ffmpeg
linux-gate.so.1 => (0x00914000)
libavdevice.so.54 => /usr/local/lib/libavdevice.so.54 (0x007d0000)
libavfilter.so.3 => /usr/local/lib/libavfilter.so.3 (0x001f3000)
libavformat.so.54 => /usr/local/lib/libavformat.so.54 (0x002b5000)
libavcodec.so.54 => /usr/local/lib/libavcodec.so.54 (0xb68dd000)
libpostproc.so.52 => /usr/local/lib/libpostproc.so.52 (0x0083c000)
libswresample.so.0 => /usr/local/lib/libswresample.so.0 (0x00a91000)
libswscale.so.2 => /usr/local/lib/libswscale.so.2 (0x00d80000)
libavutil.so.52 => /usr/local/lib/libavutil.so.52 (0x001a7000)
libm.so.6 => /lib/libm.so.6 (0x0058b000)
libpthread.so.0 => /lib/libpthread.so.0 (0x001d7000)
libc.so.6 => /lib/libc.so.6 (0x005e2000)
libasound.so.2 => /lib/libasound.so.2 (0x00ec5000)
libdc1394.so.22 => /usr/local/lib/libdc1394.so.22 (0x00116000)
librt.so.1 => /lib/librt.so.1 (0x00184000)
libfreetype.so => /usr/local/lib/libfreetype.so (0x00411000)
libass.so.4 => /usr/local/lib/libass.so.4 (0x0091a000)
libssl.so.1.0.0 => /usr/local/lib/libssl.so.1.0.0 (0x0048c000)
libcrypto.so.1.0.0 => /usr/local/lib/libcrypto.so.1.0.0 (0x00aa8000)
librtmp.so.0 => /usr/local/lib/librtmp.so.0 (0x009dd000)
libz.so.1 => /lib/libz.so.1 (0x0018d000)
libx264.so.132 => /usr/local/lib/libx264.so.132 (0x00fb1000)
libvorbisenc.so.2 => /usr/local/lib/libvorbisenc.so.2 (0x0194d000)
libvorbis.so.0 => /usr/local/lib/libvorbis.so.0 (0x004e5000)
libvo-aacenc.so.0 => /usr/local/lib/libvo-aacenc.so.0 (0x00799000)
libtwolame.so.0 => /usr/local/lib/libtwolame.so.0 (0x0050d000)
libtheoraenc.so.1 => /usr/local/lib/libtheoraenc.so.1 (0x0052d000)
libtheoradec.so.1 => /usr/local/lib/libtheoradec.so.1 (0x00779000)
libspeex.so.1 => /usr/local/lib/libspeex.so.1 (0x00c94000)
libmp3lame.so.0 => /usr/local/lib/libmp3lame.so.0 (0x0088c000)
libfaac.so.0 => /usr/local/lib/libfaac.so.0 (0x00573000)
/lib/ld-linux.so.2 (0x005c2000)
libdl.so.2 => /lib/libdl.so.2 (0x001a1000)
libraw1394.so.11 => /usr/local/lib/libraw1394.so.11 (0x005b5000)
libfribidi.so.0 => /usr/local/lib/libfribidi.so.0 (0x007b5000)
libfontconfig.so.1 => /usr/local/lib/libfontconfig.so.1 (0x007ea000)
libogg.so.0 => /usr/local/lib/libogg.so.0 (0x00583000)
libexpat.so.1 => /lib/libexpat.so.1 (0x00933000)
我的系统里没有设置LD_LIBRARY_PATH环境变量,上一篇博文里编译的ffmpeg运行时依赖的非常多的动态库。现在我们把其中的一个库libmp3lame.so.0从/usr/loca/lib下移动到/opt目录里,并执行ldconfig,让libmp3lame.so.0彻底从/etc/ld.so.cache里面消失。其实libmp3lame.so.0只是libmp3lame.so.0.0.0的一个符号链接,我们真正需要移动的是后者,完了之后再执行ldd /usr/local/bin/ffmpeg时结果如下:

[root@localhost ~]# ldd /usr/local/bin/ffmpeg
linux-gate.so.1 => (0x00249000)
libavdevice.so.54 => /usr/local/lib/libavdevice.so.54 (0x00e12000)
libavfilter.so.3 => /usr/local/lib/libavfilter.so.3 (0x00ccd000)
libavformat.so.54 => /usr/local/lib/libavformat.so.54 (0x00891000)
libavcodec.so.54 => /usr/local/lib/libavcodec.so.54 (0xb6877000)
libpostproc.so.52 => /usr/local/lib/libpostproc.so.52 (0x001a6000)
libswresample.so.0 => /usr/local/lib/libswresample.so.0 (0x00b8f000)
libswscale.so.2 => /usr/local/lib/libswscale.so.2 (0x0024a000)
libavutil.so.52 => /usr/local/lib/libavutil.so.52 (0x005d7000)
libm.so.6 => /lib/libm.so.6 (0x007ad000)
libpthread.so.0 => /lib/libpthread.so.0 (0x001f6000)
libc.so.6 => /lib/libc.so.6 (0x0029f000)
libasound.so.2 => /lib/libasound.so.2 (0x00604000)
libdc1394.so.22 => /usr/local/lib/libdc1394.so.22 (0x00436000)
librt.so.1 => /lib/librt.so.1 (0x00a06000)
libfreetype.so => /usr/local/lib/libfreetype.so (0x0052d000)
libass.so.4 => /usr/local/lib/libass.so.4 (0x00211000)
libssl.so.1.0.0 => /usr/local/lib/libssl.so.1.0.0 (0x00eed000)
libcrypto.so.1.0.0 => /usr/local/lib/libcrypto.so.1.0.0 (0x00f46000)
librtmp.so.0 => /usr/local/lib/librtmp.so.0 (0x004b9000)
libz.so.1 => /lib/libz.so.1 (0x0022a000)
libx264.so.132 => /usr/local/lib/libx264.so.132 (0x0765d000)
libvorbisenc.so.2 => /usr/local/lib/libvorbisenc.so.2 (0x00a0f000)
libvorbis.so.0 => /usr/local/lib/libvorbis.so.0 (0x004ce000)
libvo-aacenc.so.0 => /usr/local/lib/libvo-aacenc.so.0 (0x005a8000)
libtwolame.so.0 => /usr/local/lib/libtwolame.so.0 (0x006f0000)
libtheoraenc.so.1 => /usr/local/lib/libtheoraenc.so.1 (0x00710000)
libtheoradec.so.1 => /usr/local/lib/libtheoradec.so.1 (0x00756000)
libspeex.so.1 => /usr/local/lib/libspeex.so.1 (0x00770000)
libmp3lame.so.0 => not found //果然飘红了 :)
libfaac.so.0 => /usr/local/lib/libfaac.so.0 (0x004a4000)
/lib/ld-linux.so.2 (0x0050d000)
libdl.so.2 => /lib/libdl.so.2 (0x0023e000)
libraw1394.so.11 => /usr/local/lib/libraw1394.so.11 (0x004f6000)
libfribidi.so.0 => /usr/local/lib/libfribidi.so.0 (0x0078a000)
libfontconfig.so.1 => /usr/local/lib/libfontconfig.so.1 (0x007d7000)
libogg.so.0 => /usr/local/lib/libogg.so.0 (0x00243000)
libexpat.so.1 => /lib/libexpat.so.1 (0x00806000)

[root@localhost ~]# ffmpeg --help
ffmpeg: error while loading shared libraries: libmp3lame.so.0: cannot open shared object file: No such file or directory //此时ffmpeg当然运行不起来
我们来试试LD_LIBRARY_PATH,看看好使不:
[root@localhost opt]# export LD_LIBRARY_PATH=/opt:$LD_LIBRARY_PATH
[root@localhost opt]#
[root@localhost opt]# ldd /usr/local/bin/ffmpeg
linux-gate.so.1 => (0x00136000)
libavdevice.so.54 => /usr/local/lib/libavdevice.so.54 (0x00552000)
libavfilter.so.3 => /usr/local/lib/libavfilter.so.3 (0x00655000)
libavformat.so.54 => /usr/local/lib/libavformat.so.54 (0x00243000)
libavcodec.so.54 => /usr/local/lib/libavcodec.so.54 (0xb68a7000)
libpostproc.so.52 => /usr/local/lib/libpostproc.so.52 (0x00137000)
libswresample.so.0 => /usr/local/lib/libswresample.so.0 (0x00187000)
libswscale.so.2 => /usr/local/lib/libswscale.so.2 (0x0047e000)
libavutil.so.52 => /usr/local/lib/libavutil.so.52 (0x00a9d000)
libm.so.6 => /lib/libm.so.6 (0x00af9000)
libpthread.so.0 => /lib/libpthread.so.0 (0x00823000)
libc.so.6 => /lib/libc.so.6 (0x0083e000)
libasound.so.2 => /lib/libasound.so.2 (0x0055f000)
libdc1394.so.22 => /usr/local/lib/libdc1394.so.22 (0x0019e000)
librt.so.1 => /lib/librt.so.1 (0x00b3c000)
libfreetype.so => /usr/local/lib/libfreetype.so (0x0039f000)
libass.so.4 => /usr/local/lib/libass.so.4 (0x00f67000)
libssl.so.1.0.0 => /usr/local/lib/libssl.so.1.0.0 (0x00cb3000)
libcrypto.so.1.0.0 => /usr/local/lib/libcrypto.so.1.0.0 (0x00d0c000)
librtmp.so.0 => /usr/local/lib/librtmp.so.0 (0x0020c000)
libz.so.1 => /lib/libz.so.1 (0x00c77000)
libx264.so.132 => /usr/local/lib/libx264.so.132 (0x00f80000)
libvorbisenc.so.2 => /usr/local/lib/libvorbisenc.so.2 (0x07c66000)
libvorbis.so.0 => /usr/local/lib/libvorbis.so.0 (0x0041a000)
libvo-aacenc.so.0 => /usr/local/lib/libvo-aacenc.so.0 (0x0076c000)
libtwolame.so.0 => /usr/local/lib/libtwolame.so.0 (0x004fe000)
libtheoraenc.so.1 => /usr/local/lib/libtheoraenc.so.1 (0x00717000)
libtheoradec.so.1 => /usr/local/lib/libtheoradec.so.1 (0x00f0c000)
libspeex.so.1 => /usr/local/lib/libspeex.so.1 (0x00221000)
libmp3lame.so.0 => not found //纳尼??!!!
libfaac.so.0 => /usr/local/lib/libfaac.so.0 (0x00124000)
/lib/ld-linux.so.2 (0x00bad000)
libdl.so.2 => /lib/libdl.so.2 (0x0023b000)
libraw1394.so.11 => /usr/local/lib/libraw1394.so.11 (0x007b6000)
libfribidi.so.0 => /usr/local/lib/libfribidi.so.0 (0x00442000)
libfontconfig.so.1 => /usr/local/lib/libfontconfig.so.1 (0x0051e000)
libogg.so.0 => /usr/local/lib/libogg.so.0 (0x009f7000)
libexpat.so.1 => /lib/libexpat.so.1 (0x00b60000)
还记得上面提到了软链接么,libmp3lame.so.0就是libmp3lame.so.0.0.0的软链接,这是动态库的命名规范的一种公约,我们只要在/opt/目录下建立一个名为libmp3lame.so.0的到/opt/libmp3lame.so.0.0.0的软链接就OK了:

[root@localhost opt]# ls
libmp3lame.so.0.0.0
[root@localhost opt]# ln -s libmp3lame.so.0.0.0 libmp3lame.so.0
[root@localhost opt]# ll
total 316
lrwxrwxrwx. 1 root root 19 Dec 7 23:27 libmp3lame.so.0 -> libmp3lame.so.0.0.0
-rwxr-xr-x. 1 root root 321228 Dec 7 23:25 libmp3lame.so.0.0.0
[root@localhost opt]# ldd /usr/local/bin/ffmpeg
linux-gate.so.1 => (0x00cc4000)
libavdevice.so.54 => /usr/local/lib/libavdevice.so.54 (0x00577000)
libavfilter.so.3 => /usr/local/lib/libavfilter.so.3 (0x00e3f000)
libavformat.so.54 => /usr/local/lib/libavformat.so.54 (0x00202000)
libavcodec.so.54 => /usr/local/lib/libavcodec.so.54 (0x00f01000)
libpostproc.so.52 => /usr/local/lib/libpostproc.so.52 (0x00170000)
libswresample.so.0 => /usr/local/lib/libswresample.so.0 (0x00750000)
libswscale.so.2 => /usr/local/lib/libswscale.so.2 (0x0035e000)
libavutil.so.52 => /usr/local/lib/libavutil.so.52 (0x005ba000)
libm.so.6 => /lib/libm.so.6 (0x00452000)
libpthread.so.0 => /lib/libpthread.so.0 (0x001c0000)
libc.so.6 => /lib/libc.so.6 (0x008c2000)
libasound.so.2 => /lib/libasound.so.2 (0x0047c000)
libdc1394.so.22 => /usr/local/lib/libdc1394.so.22 (0x003d6000)
librt.so.1 => /lib/librt.so.1 (0x00db3000)
libfreetype.so => /usr/local/lib/libfreetype.so (0x00a80000)
libass.so.4 => /usr/local/lib/libass.so.4 (0x001db000)
libssl.so.1.0.0 => /usr/local/lib/libssl.so.1.0.0 (0x005e7000)
libcrypto.so.1.0.0 => /usr/local/lib/libcrypto.so.1.0.0 (0x00afb000)
librtmp.so.0 => /usr/local/lib/librtmp.so.0 (0x00584000)
libz.so.1 => /lib/libz.so.1 (0x00599000)
libx264.so.132 => /usr/local/lib/libx264.so.132 (0x02bc9000)
libvorbisenc.so.2 => /usr/local/lib/libvorbisenc.so.2 (0x05ccd000)
libvorbis.so.0 => /usr/local/lib/libvorbis.so.0 (0x00640000)
libvo-aacenc.so.0 => /usr/local/lib/libvo-aacenc.so.0 (0x00834000)
libtwolame.so.0 => /usr/local/lib/libtwolame.so.0 (0x00668000)
libtheoraenc.so.1 => /usr/local/lib/libtheoraenc.so.1 (0x00688000)
libtheoradec.so.1 => /usr/local/lib/libtheoradec.so.1 (0x006ce000)
libspeex.so.1 => /usr/local/lib/libspeex.so.1 (0x00815000)
libmp3lame.so.0 => /opt/libmp3lame.so.0 (0x00767000) //终于圆满了:)
libfaac.so.0 => /usr/local/lib/libfaac.so.0 (0x006e8000)
/lib/ld-linux.so.2 (0x003b6000)
libdl.so.2 => /lib/libdl.so.2 (0x001f4000)
libraw1394.so.11 => /usr/local/lib/libraw1394.so.11 (0x00444000)
libfribidi.so.0 => /usr/local/lib/libfribidi.so.0 (0x006f8000)
libfontconfig.so.1 => /usr/local/lib/libfontconfig.so.1 (0x00710000)
libogg.so.0 => /usr/local/lib/libogg.so.0 (0x001f9000)
libexpat.so.1 => /lib/libexpat.so.1 (0x007e3000)


所以说,针对动态库路径查找的种种问题,无非就这么两大类,关键是找对原因,对症下药,方能药到病除。

 

 

没有root权限解决共享库查找并使用的问题

ll /etc/ld.so.conf
-rw-r--r-- 1 root root 64 May 7 13:28 /etc/ld.so.conf

 

没有root权限运行软件时,Linux也为我们提供了一个名为LD_LIBRARY_PATH的环境变量来解决运行时动态库查找路径的解决方案。同样地,由这个环境变量所指定的路径会被装载器/lib/ld-2.12.so优先查找,然后才是动态库缓存文件/etc/ld.so.cache,风采瞬间就被LD_LIBRARY_PATH给抢完了,/etc/ld.so.cache表示很不高兴。针对LD_LIBRARY_PATH环境变量这种情况,绝对是临时不能再临时解决方案了,如果只是测试用,用export像解决PKG_CONFIG_PATH一样的方式干净利索就行了,千万不要在实际生产上线的运维环境里把export LD_LIBRARY_PATH=

 

export LD_LIBRARY_PATH=共享库地址 (/lib64/libc.so.6 )

unset LD_LIBRARY_PATH

很多命令都依赖libc.so.6 动态连接器

ldd /bin/ls
linux-vdso.so.1 => (0x00007fff446d9000)
libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fd04a836000)
librt.so.1 => /lib64/librt.so.1 (0x00007fd04a62e000)
libcap.so.2 => /lib64/libcap.so.2 (0x00007fd04a429000)
libacl.so.1 => /lib64/libacl.so.1 (0x00007fd04a221000)
libc.so.6 => /lib64/libc.so.6 (0x00007fd049e8d000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007fd049c88000)
/lib64/ld-linux-x86-64.so.2 (0x00007fd04aa66000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fd049a6b000)
libattr.so.1 => /lib64/libattr.so.1 (0x00007fd049866000)


ldd /bin/echo
linux-vdso.so.1 => (0x00007fffc43ff000)
libc.so.6 => /lib64/libc.so.6 (0x00007ff982abe000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff982e63000)


ldd /bin/mv
linux-vdso.so.1 => (0x00007fffff7ff000)
libselinux.so.1 => /lib64/libselinux.so.1 (0x00007fb609ce3000)
librt.so.1 => /lib64/librt.so.1 (0x00007fb609adb000)
libacl.so.1 => /lib64/libacl.so.1 (0x00007fb6098d2000)
libattr.so.1 => /lib64/libattr.so.1 (0x00007fb6096cd000)
libc.so.6 => /lib64/libc.so.6 (0x00007fb609339000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007fb609134000)
/lib64/ld-linux-x86-64.so.2 (0x00007fb609f13000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fb608f17000)

f

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值