【Linux】动、静态库总结

简介: 我们在编写一个程序的时候,经常会遇到好多重复或常用的部分,如果每次都重新编写固然是可以的,不过那样会大大降低工作效率,并且影响代码的可读性,更不利于后期的代码维护。我们可以把他们制作成相应的功能函数,使用时直接调用就会很方便,还可以进行后期的功能升级。

一. 什么是动、静态库?


1、库


我们在编写一个程序的时候,经常会遇到好多重复或常用的部分,如果每次都重新编写固然是可以的,不过那样会大大降低工作效率,并且影响代码的可读性,更不利于后期的代码维护。我们可以把他们制作成相应的功能函数,使用时直接调用就会很方便,还可以进行后期的功能升级。

库通俗的说就是把这些常用函数的目标文件打包在一起,即库的本质为一堆.o文件的集合。不包含main但是包含了大量的方法,提供相应函数的接口,便于程序员使用。库是别人写好的现有的,成熟的,可以复用的代码,我们只需要知道其接口如何定义,便可以自如使用。比如我们常使用的printf函数,就是 C 标准库提供的函数。我们在使用时只需要包含相应的头文件就可以使用,在连接阶段会连接到具体printf的方法上去,而不用关心printf函数具体是如何实现的,这样就大大提高了程序员编写代码的效率。

从连接方式上库大体上可以分为两类:静态库和动态库。

  • 在windows中静态库是以 .lib为后缀的文件,动态库是以 .dll为后缀的文件。
  • 在linux中静态库是以 .a为后缀的文件,动态库是以 .so为后缀的文件。

PS:下面我们讨论的都是Linux的库。

库的连接方式

一个程序从源文件编译生成可执行文件的步骤:

bfa611b123ae4bee9cd6e05b19f08b3d.png

其中链接将二进制文件链接成一个可执行的命令,主要是把分散的数据和代码收集并合成一个单一的可加载并可执行的的文件。发生在代码静态编译、程序被加载时的连接称为静态连接,对应连接的库叫做静态库;发生在程序执行时的连接称为动态连接,对应连接的库称为动态库。


2、动、静态库


静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。

动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序可以共享使用库的代码。


3、库相关的两个命令


file命令

file命令用来识别文件类型,也可用来辨别一些文件的编码格式。这里我们可以通过file命令查看可执行程序的连接方式。

645d1f75891b47249dd206f01e9a831b.png

默认情况下库的连接方式都是动态链接,如果需要静态连接我们需要在编译时加上“-static”选项。

091d62bfd9f24b40aad9bf37017a18d8.png

对比同一个源文件静态连接生成的可执行程序大小差不多是动态连接的20倍,因为在静态链接时程序把库的代码链接到可执行程序中了。

ldd命令

全称list dynamic dependence,该命令可以查看一个可执行程序依赖的动态库。

32a3819a44f7451aabf0f84c72eae755.png


4. Linux中库的标准命名


静态库的名字一般为libxxx.a,其中xxx是该lib的名称。

动态库的名字一般为libxxx.so.major.minor,xxx是该lib的名称,major是主版本号, minor是副版本号。

4a380547ad264181b101df117dbdcd0a.png


二. 动、静态库的优缺点


动态库


优点

  • 节省内存空间:运行时除了代码加载到内存,其所使用的动态库也会被加到内存,如果其他进程也需要使用该动态库,则只要调整其他进程的映射关系到已经在内存加载好的库即可,不需要重复加载,节省内存空间。
  • 将一些程序升级变得简单,用户只需要更新动态库即可。

缺点:依赖库,如果可执行程序生成后,删除库则无法运行这个可执行程序。


动态库


优点:可执行程序的运行与库无关,因为在编译连接时库已经链接到可执行程序中,删除库后仍可运行。

缺点

  • 多个进程使用同一库会导致内存资源浪费。
  • 静态库对程序的更新、部署和发布页会带来麻烦。如果静态库更新了,所有使用它的应用程序都需要重新编译、发布给用户(对于玩家来说,可能是一个很小的改动,却导致整个程序重新下载,全量更新)。


三. 动、静态库的实现


打包自己的库,这样我们就可以把我们自己写的函数给其他人使用了,比如我们写了一个加法函数如下:把该函数的声明放到头文件里,实现放到一个源文件里。

cefe31b315c24ae19244f6e65f40c632.png

我们最终想要实现的是只要包含了这个库的头文件就可以使用这个库的接口函数。

14bf7e9868e040fc8f0c3c3a29667fa4.png

1、静态库实现


第一步:汇编生成目标文件(g++ -c +源文件 -o +生成的目标文件名称)

对写有函数实现的源文件使用g++配合-c选项汇编生成相应的目标文件:

a7ac88f5f6a24edf912bcaf7122a7d9e.png

第二步:使用ar命令打包静态库(ar -rc +静态库名称 +所有依赖的.o文件)

ar可以集合许多文件,成为单一的备存文件。在备存文件中,所有成员文件皆保有原来的属性与权限。

  • 选项里的r表示将文件插入备存文件中的意思,c表示建立备存文件。
  • 注意顺序必须是静态库名称在前,目标文件的名称在后。
  • 可以有多个目标文件。

0bdb8bdeacdf45889e0a4c7ed45c2364.png

打包好了库之后,我们把库和头文件封装到一个目录里,并删除一些其他不用的文件:

40cc09d338fc4b5eb1ee1d06acaa6e6c.png

第三步:生成静态可执行程序(g++ +源文件名称 -I +头文件路径 -L +库路径 -l +要链接的库名 -o +生成的可执行程序名称)

这里指明库、头文件路径和库名称是要告诉编译器和连接器,保证在它们在工作时能够找到相应的库的数据。另外这里的库名称是指省略前缀lib和后缀.a之后的真正库的名称。

55a7339bcc8d4521b26dc9fc8c9f7506.png

还有一种方法可以生成可执行程序时不加库路径和头文件路径,那就是直接把库和头文件拷贝到系统默认查找路径下,系统会直接到这个默认路径下查找相应文件,但不建议这样做因为这会污染路径源。

头文件的系统默认查找路径:/usr/include

库文件的系统默认查找路径:/lib64

9a1b988275014c23af01421d32f23691.png

之后在生成可执行程序时不需要指明库路径和头文件路径,但还是要指明需要连接的库的名称。

b7db11fe8b944939b889172958d28614.png


2、动态库实现

第一步:汇编生成目标文件(g++ -c -fPIC +源文件)

注意在生成.o文件时要生成位置无关码,要在g++后加 -fPIC表示生成位置无关码,这样的代码无绝对跳转,跳转都为相对跳转。

4eb2803e354340bc9223f65e607f92f4.png

fPIC 的全称是 Position Independent Code,加了 fPIC 实现真正意义上的多个进程共享 .so 文件,当多个进程引用同一个 PIC 动态库时,可以共用内存。这一个库在不同进程中的虚拟地址不同,但操作系统会把它们映射到同一块物理内存上。

如果不加 fPIC,则加载 .so 文件时,需要对代码段引用的数据对象需要重定位,重定位会修改代码段的内容,根据写实拷贝的规则,这就造成每个使用这个 .so 文件代码段的进程在内核里都会生成这个 .so 文件代码段的 copy。每个 copy 都不一样,取决于这个 .so 文件代码段和数据段内存映射的位置。

第二步:使用g++打包动态库(g++ -share +所有依赖的.o文件 -o +动态库标准名称)

-shared选项表示创建一个动态链接库。区分这里打包库的方式是使用g++,而打包静态库的方式是使用ar命令。

75c2a457fe624471be4eadeeeb5d2638.png

整理刚刚生成的库文件和库的头文件并删除一些不用的文件:

828456bb1a484eea846c8028278f2f99.png

第三步:生成动态可执行程序(g++ +源文件名称 -I +头文件路径 -L +库路径 -l +要链接的库名 -o +生成的可执行程序名称)

这一步的操作方法和生成静态可执行程序一样:指明头文件,库路径和库名称。

288fa36c48994690b8b32a9b5023cc17.png

但是生成的这个可执行程序是不能运行的,虽然它通过了编译器的编译器的语法检查,但是在运行的过程中,操作系统无法查找到可执行程序所依赖的库。

用ldd命令查看该可执行程序的动态依赖关系发现我们自己的动态库没有被找到。

dfeee36880e3407c8fd52357198d712e.png

第四步:解决动态库生成的可执行程序无法运行的问题

①将依赖的库拷贝到系统路径下

6defac5efa9548f080390ae3b1fc7b49.png

之后操作系统在运行的时候能够在/lib64中找到其依赖的库的路径,不会这样做会污染路径源。

d72b87aa4d66425c9822fd9b5b15a62f.png

②更改环境变量(LD_LIBRARY_PATH)

把库的绝对路径导入到环境变量LD_LIBRARY_PATH中:

fc218b59db2346baa5a59a7d854f4ad1.png

这样操作系统就可以通过LD_LIBRARY_PATH找到库的路径,不过这样导入的环境变量只是临时的,下次登录后会被清除。

26b1d76656d24f5aa8dccac4bbc4ff21.png

③更新ldconfig配置

将自己库的路径写到配置文件/etc/ld.so.conf里

90b3d7f8b463460dba3f8e6214318b8d.png

再使用命令ldconfig刷新动态库,之后操作系统就会在/etc/ld.so.conf里找到对应配置文件里保存的库的路径里。

f9d5eedfd13e4024989f1f54d5d65708.png

相关文章
|
6月前
|
安全 Linux vr&ar
Linux的动态库和静态库
Linux的动态库和静态库
|
2月前
|
存储 编译器 Linux
动态链接的魔法:Linux下动态链接库机制探讨
本文将深入探讨Linux系统中的动态链接库机制,这其中包括但不限于全局符号介入、延迟绑定以及地址无关代码等内容。
849 25
|
4月前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
142 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
6月前
|
Linux API
在Linux中,程序产生了库日志虽然删除了,但磁盘空间未更新是什么原因?
在Linux中,程序产生了库日志虽然删除了,但磁盘空间未更新是什么原因?
|
7月前
|
Oracle 关系型数据库 Linux
讲解linux下的Qt如何编译oracle的驱动库libqsqloci.so
通过这一连串的步骤,可以专业且有效地在Linux下为Qt编译Oracle驱动库 `libqsqloci.so`,使得Qt应用能够通过OCI与Oracle数据库进行交互。这些步骤适用于具备一定Linux和Qt经验的开发者,并且能够为需要使用Qt开发数据库应用的专业人士提供指导。
239 1
讲解linux下的Qt如何编译oracle的驱动库libqsqloci.so
|
6月前
|
Linux 网络安全 API
【Azure 应用服务】App Service For Linux 环境中,如何从App Service中获取GitHub私有库(Private Repos)的Deploy Key(RSA key)呢?
【Azure 应用服务】App Service For Linux 环境中,如何从App Service中获取GitHub私有库(Private Repos)的Deploy Key(RSA key)呢?
|
8月前
|
存储 Linux C语言
Linux|如何安装和运行多个 glibc 库
Linux|如何安装和运行多个 glibc 库
1619 5
|
6月前
|
小程序 Linux 开发者
Linux之缓冲区与C库IO函数简单模拟
通过上述编程实例,可以对Linux系统中缓冲区和C库IO函数如何提高文件读写效率有了一个基本的了解。开发者需要根据应用程序的具体需求来选择合适的IO策略。
51 0
|
8月前
|
Linux 编译器 C语言
Linux中的pkg-config:简化库依赖管理的利器
**pkg-config**是Linux下管理库依赖的工具,它通过读取库的`.pc`文件提供编译和链接参数。使用`pkg-config --cflags --libs <library>`获取编译和链接选项,例如`gcc -o test test.c $(pkg-config --cflags --libs glib-2.0)`。能进行版本检查、参数提取、依赖管理和路径搜索。列出所有包用`pkg-config --list-all`。最佳实践包括确保库正确安装、检查版本、配置`PKG_CONFIG_PATH`及使用构建工具。
|
8月前
|
NoSQL Linux C语言
Linux gdb调试的时候没有对应的c调试信息库怎么办?
Linux gdb调试的时候没有对应的c调试信息库怎么办?
64 1