了解Linux如何使用库,包括静态库和动态库的区别,帮助你解决依赖问题。Linux在某种意义上是一堆相互依赖的静态库和动态库。对于刚接触Linux系统的人来说,库的整个过程有点神秘。但对于有经验的人来说,操作系统内置的大量共享代码在编写新应用程序时是一个优势。为了让您熟悉该主题,我准备了一个小应用示例来展示如何在常见的Linux发行版上处理库(未在其他操作系统上验证)。要使用此示例学习本实践教程,请打开命令行并键入:.c-olibmy_static_a.o-Wall-Werrorcc-clibmy_static_b.c-olibmy_static_b.o-Wall-Werrorar-rsvlibmy_static.alibmy_static_a.olibmy_static_b.oar:创建libmy_static.aa-libmy_static_a.oa-libb.ocmy_c-c-fPIClibmy_shared.c-olibmy_shared.occ-shared-olibmy_shared.solibmy_shared.o$makecleanrm*.o执行完这些命令后,应该在目录下添加这些文件(执行ls查看):my_applibmy_static.alibmy_shared.so关于静态链接当你的应用程序链接一个静态库时,这个库的代码就成为了可执行文件的一部分。此操作在链接过程中只执行一次,这些静态库通常以.a扩展名结尾。静态库是多个目标对象文件的存档(ar)。这些目标文件通常是ELF格式。ELF是ExecutableandLinkableFormat的缩写,兼容多种操作系统。file命令的输出可以告诉你静态库libmy_static.a是一个ar格式的归档文件类型。$filelibmy_static.alibmy_static.a:currentararchive使用ar-t你可以看到存档的内部。它显示了两个目标文件:$ar-tlibmy_static.alibmy_static_a.olibmy_static_b.o您可以使用ax-x命令提取存档文件。所有建议的目标文件都是ELF格式:$ar-xlibmy_static.a$filelibmy_static_a.olibmy_static_a.o:ELF64-bitLSBrelocatable,x86-64,version1(SYSV),notstrippedaboutdynamiclinkingdynamiclinking参考共享库的使用。共享库通常以.so扩展名(“共享对象”的缩写)结尾。共享库是Linux系统中最常见的依赖管理方法。这些共享库在应用程序启动之前加载到内存中。当多个应用程序需要同一个库时,该库只会在系统中加载一次。此功能减少了应用程序的内存占用。另一件值得注意的事情是,当共享库中的错误被修复时,所有引用该库的应用程序都会受益。但这也意味着,如果一个bug没有被发现,所有相关的应用程序都会遭受这个bug(如果应用程序使用了受影响的部分)。当一个应用程序需要某个版本的库,但链接器只知道不兼容版本的位置时,这个问题对于初学者来说是非常棘手的。在这种情况下,您必须帮助链接器找到正确版本的路径。虽然这不是日常问题,但了解动态链接的工作原理总能帮助您解决类似问题。幸运的是,动态链接的机制其实非常简单明了。要检查应用程序在启动时需要哪些库,可以使用ldd命令,它将打印出给定文件所需的动态库:$lddmy_applinux-vdso.so.1(0x00007ffd1299c000)libmy_shared.so=>notfoundlibc.so.6=>/lib64/libc.so.6(0x00007f56b869b000)/lib64/ld-linux-x86-64.so.2(0x00007f56b8881000)您可以注意到libmy_shared.so库是代码库,但是没有找到。这是因为负责在应用程序启动之前将所有依赖项加载到内存中的动态链接器没有在它搜索的标准路径中找到这个库。与常用库(如bizp2)的不兼容版本相关的问题可能会让新手感到困惑。一种方法是将存储库的路径添加到环境变量LD_LIBRARY_PATH中,以告诉链接器在哪里可以找到正确的版本。在这种情况下,正确的版本在此目录中,因此您可以将其导出到环境变量:$LD_LIBRARY_PATH=$(pwd):$LD_LIBRARY_PATH$exportLD_LIBRARY_PATH现在动态链接器知道在哪里可以找到库,应用程序可以执行。您可以再次执行ldd以调用动态链接器,这将检查应用程序的依赖项并将它们加载到内存中。对象路径后会显示内存地址:$lddmy_applinux-vdso.so.1(0x00007ffd385f7000)libmy_shared.so=>/home/stephan/library_sample/libmy_shared.so(0x00007f3fad401000)libc.so.6=>/lib64/libc.so.6(0x00007f3fad21d000)/lib64/ld-linux-x86-64.so.2(0x00007f3fad408000)要知道调用了哪个链接器,可以使用文件命令:$filemy_appmy_app:ELF64-位LSB可执行文件,x86-64,版本1(SYSV),动态链接,解释器/lib64/ld-linux-x86-64.so.2,BuildID[sha1]=26c677b771122b4c99f0fd9ee001e6c743550fa6,适用于GNU/Linux3.2.0,未剥离linker/lib64/ld-linux-x86–64.so.2是指向ld-2.30.so的软链接,它也是我的Linux发行版的默认链接器:$file/lib64/ld-linux-x86-64。so.2/lib64/ld-linux-x86-64.so.2:指向ld-2.31.so的符号链接回头看ldd命令的输出,还可以看到(在libmy_shared.so旁边)每个依赖的两端带有一个数字(例如/lib64/libc.so.6)。共享库常用的命名格式是:libXYZ.so..在我的系统中,libc.so.6也是一个软链接,指向同目录下的共享库libc-2.31.so。$file/lib64/libc.so.6/lib64/libc.so.6:symboliclinktolibc-2.31.so如果你遇到因为加载的库版本错误而导致应用程序无法启动,那就很可能您可以通过检查以整理这些符号链接或确定正确的搜索路径来解决此问题(请参阅下面的“动态加载程序:ld.so”部分)。有关详细信息,请参见ldd手册页。动态加载动态加载是指在程序运行时加载一个库(如.so文件)。这是使用一定的编程方法实现的。当应用程序使用可在运行时更改的插件时,将使用动态加载。有关详细信息,请参阅dlopen手册页。动态加载器:ld.so在Linux系统中,您几乎总是与共享库打交道,因此必须有一种机制来检测应用程序的依赖项并将它们加载到内存中。ld.so按以下顺序在这些地方查找共享对象:在应用程序的绝对路径或相对路径下(使用GCC编译器的-rpath选项进行硬编码)环境变量LD_LIBRARY_PATH/etc/ld.so.cache文件需要记住的是,将库添加到系统库存档/usr/lib64需要管理员权限。您可以手动将libmy_shared.so复制到库存档中以使应用程序可运行,而无需设置LD_LIBRARY_PATH。unsetLD_LIBRARY_PATHsudocplibmy_shared.so/usr/lib64/运行ldd时,您现在可以看到显示的存档库路径:$lddmy_applinux-vdso.so.1(0x00007ffe82fab000)libmy_shared.so=>/lib64/libmy_shared.so(0x00007f0a963e0000)libc.so.6=>/lib64/libc.so.6(0x00007f0a96216000)/lib64/ld-linux-x86-64.so.2(0x00007f0a96401000)如果编译时自定义共享库为了让您的应用程序使用您的共享库,您可以在编译时指定绝对或相对路径。编辑makefile(第10行)并使用make-B重新编译程序。然后ldd输出显示libmy_shared.so与其绝对路径一起列出。将此更改:CFLAGS=-Wall-Werror-Wl,-rpath,$(shellpwd)到此(记得更改用户名):CFLAGS=/home/stephan/library_sample/libmy_shared.so并重新编译:$make以确认它正在使用您设置的绝对路径,如您在第二行输出中所见:$lddmy_applinux-vdso.so.1(0x00007ffe143ed000)libmy_shared.so=>/lib64/libmy_shared.so(0x00007fe50926d000)/home/stephan/library_sample/libmy_shared.so(0x00007fe509268000)libc.so.6=>/lib64/libc.so.6(0x00007fe50909e000)/lib64/ld-linux-x86-64.so.2(0x00007fe50928e000)这是一个很好的例子,但是如果你正在编写一个供其他人使用的库,它是如何工作的?可以通过写入/etc/ld.so.conf或创建包含/etc/ld.so.conf.d/目录中的路径的.conf文件向系统注册新库的路径.之后,您必须执行ldconfig命令覆盖ld.so.cache文件。有时安装带有特殊共享库的程序时不能省略此步骤。有关更多详细信息,请参见ld.so的手册页。如何处理多种架构通常,应用程序的32位和64位版本有不同的库。以下列表显示了不同Linux发行版的标准库路径:RedHat系列32位:/usr/lib64位:/usr/lib64Debian系列32位:/usr/lib/i386-linux-gnu64-位:/usr/lib/x86_64-linux-gnuArchLinux系列32位:/usr/lib3264位:/usr/lib64FreeBSD(技术上不是Linux发行版)32位:/usr/lib3264位:/usr/lib知道该去哪里找到这些关键的库可以让库链接失败的问题成为历史。虽然一开始很困惑,但了解Linux库的依赖管理是对操作系统有控制感的标志。在其他应用程序中运行这些步骤以熟悉常用库,然后继续学习如何解决您可能遇到的任何库挑战。