【Linux从入门到精通】动静态库的原理与制作详解

news2024/11/15 12:12:12

  

  本篇文章主要是围绕动静态库的原理与制作进行展开讲解的。其中涉及到了inode的概念引入和软硬连接的讲解。会结合实际操作对这些抽象的概念进行解释,希望会对你有所帮助。

文章目录

一、inode 概念

二、软硬链接

2、1 软连接

2、2 硬链接

三、动静态库概念

3、1 静态库制作

3、2 静态库的使用

3、2、1 加载到系统的文件中 

3、2、2 指定路径直接使用

3、3 动态库制作

3、4 动态库的使用

3、4、1 指定路径直接使用

3、4、2 环境变量:LD_LIBRARY_PATH 

3、4、3 系统文件:/etc/ld.so.conf.d/ 

 四、总结


🙋‍♂️ 作者:@Ggggggtm 🙋‍♂️

👀 专栏:Linux从入门到精通  👀

💥 标题:动静态库💥

 ❣️ 寄语:与其忙着诉苦,不如低头赶路,奋路前行,终将遇到一番好风景 ❣️ 

一、inode 概念

  一个文件里面没有任何内容,文件的大小就是0吗?实际上并不是。我们知道,文件不仅仅要保存其内容,属性也是必不可少的。属性保存在哪里呢?

  Inode(Index Node)是文件系统中的一个重要概念,用于存储和管理文件的元数据。在UNIX和类UNIX操作系统中,如Linux,每个文件都与一个唯一的inode相关联。

Inode 包含了以下文件的元数据信息:

  1. 文件类型:指示文件的类型,如常见的普通文件、目录、符号链接等。
  2. 文件权限:描述了对文件的访问权限,包括拥有者、所属组和其他用户的读、写、执行权限。
  3. 文件大小:表示文件占用的磁盘空间大小。
  4. 日期和时间戳:记录了文件的创建时间、最近修改时间和最近访问时间。
  5. 硬链接计数:记录有多少个硬链接指向该文件。硬链接是指多个文件名指向同一个inode的情况。
  6. 文件数据块的物理地址:指示文件数据在磁盘上的存储位置。

  通过使用inode,操作系统可以有效地管理文件系统中的文件。例如,当需要读取或写入文件时,操作系统可以根据文件的inode查找文件的数据块的物理位置,以快速定位并访问文件内容。

  需要注意的是,inode与文件名是独立的。文件名与inode之间的对应关系由文件系统的目录结构来维护。当文件被打开或者通过文件名访问时,操作系统会根据文件系统的目录结构使用inode来定位文件。

二、软硬链接

2、1 软连接

  软连接(Symbolic Link)是一个指向另一个文件或目录的链接。它是一个特殊类型的文件,其中包含了所指向文件或目录的路径信息。软连接可以跨越不同的文件系统,甚至可以指向不存在的文件或目录。删除软连接不会影响被链接的文件或目录本身,而只会删除软连接本身。下面我们看一个实际的例子。

  如上图,我们在上图的路径中有一个可执行程序 mytest。假设我们想在其他路径下执行该程序,一种办法就是使用 相对路径/绝对路径 找到该可执行程序。具体如下图:

  但是每次都需要加上路径去执行该程序是不是优点太过繁杂。那有没有其他的便捷的方法呢?这里就可使用到软连接:ln -s ./test_8_19/bin/mytest mytest。具体如下图:

  对这种方法有一种莫名熟悉的感觉。在windows下,不就是在桌面创建快捷方式嘛!!!

软连接的主要特点和用途包括:

  • 文件间的共享:通过创建软连接,多个文件可以引用同一个文件,减少存储空间的占用。
  • 简化路径:软连接可以提供简洁的路径名,使得访问文件更加方便。
  • 跨文件系统的链接:软连接可以跨越不同的文件系统进行链接,增强了灵活性。
  • 动态更新:当原始文件或目录发生改变时,软连接也会实时更新,保持链接的有效性。

2、2 硬链接

  硬连接(Hard Link)是一个直接指向目标文件或目录的链接。硬连接与原始文件或目录没有区别,它们共享同一个索引节点,指向同一个磁盘区域,从而形成了相同的文件内容和属性。删除硬链接不会影响原始文件或目录,因为硬链接实际上是原始文件或目录的另一个名称。

  如上图,我们使用link指令创建了一个硬链接。我们发现他们的inode竟然相同。那么创建硬链接,并不是真正的创建文件。而是在目录下,建立了文件名与指定inode的映射关系而已!通俗理解,就是给指定文件起别名

  硬链接的主要特点和用途包括:

  • 文件备份:通过创建硬链接,可以在不占用额外存储空间的情况下,生成与原始文件内容完全相同的备份文件。
  • 文件共享:多个硬链接可以引用同一个文件,可以在不同位置使用相同的文件。
  • 快速访问:由于硬链接实际上是同一个文件,所以可以通过多个链接快速访问文件,提高效率。(隐藏文件 . ..)

  需要注意的是:

  • 软连接可以跨越不同的文件系统进行链接,而硬连接只能在同一文件系统中创建链接。
  • 删除原始文件并不会立即影响已经创建的硬链接,因为硬链接与原始文件共享相同的磁盘空间,只有当所有链接都被删除后,才会真正释放磁盘空间。
  • 软连接可以指向不存在的文件或目录,而硬连接必须指向已存在的文件或目录。

三、动静态库概念

3、1 静态库制作

  静态库是一种将一组预编译的目标文件(.o)打包成一个单独的文件的技术。它的主要作用是将代码模块化并提供给其他开发者使用,以便在编译阶段将这些模块链接到他们的程序中。

  静态库的形成: 静态库是由多个编译好的目标文件组成的,这些目标文件包含了被编译源代码的函数和数据。当我们将这些目标文件打包成一个单独的库文件时,就形成了静态库。通常,静态库的文件扩展名是.a(在Windows上也可以是.lib

形成静态库的过程包括:

  • 预处理:处理源代码中的宏定义、条件编译等预处理指令。
  • 编译:将预处理后的源代码编译成汇编代码。
  • 汇编:将汇编代码转换成机器码,并生成目标文件(通常是.o文件)。
  • 链接:将多个目标文件链接在一起形成静态库文件。

  如下图,我们经过编译生成了目标文件(.o)。

  我们再对 .o 文件打包生成静态库:ar -rc libhello.a mymath.o myprint.o。

  上述打包的过程就是生成静态库的过程。通俗来讲,静态库就是对.o 文件进行打包形成的

  我们所生成静态库的名字前缀必须是lib,后缀必须是.a 。这个就是规定。

  为了方便使用,我们将静态库和头文件统一放到一个目录下。具体操作如下:

libhello.a:mymath.o myprint.o
	ar -rc libhello.a mymath.o myprint.o

mymath.o:mymath.c
	gcc -c mymath.c -o mymath.o

myprint.o:myprint.c
	gcc -c myprint.c -o myprint.o

.PHONY:hello
hello:
	mkdir -p hello/lib
	mkdir -p hello/include
	cp -rf *.h hello/include
	cp -rf *.a hello/lib

.PHONY:clean
clean:
	rm -rf *.a *.o hello

3、2 静态库的使用

  我们再把打包静态库和头文件目录(hello)拷贝到上级目录下一个文件中进行调用使用。

  我们直接编译main.c 文件,可以吗?看下图:

  是不可以的。编译时会自动在本目录下查找头文件。如果找不到,就回去系统的文件中查找。该目录下确实是没有,所以试过报错的。那怎么才能使用呢?

3、2、1 加载到系统的文件中 

  gcc 编译时,头文件的默认搜索路径是:/usr/include。库文件的默认搜索路径是:/lib64。当我们把自己所写的头文件和静态库加载到系统默认的搜索路径后,就会自动找到我们所引入的头文件和库函数。具体操作如下:

  但是此时我们进行编译,还是会进行报错。如下图:

  这是为什么呢?我们自己写的库属于第三方库,在编译时必须需要指定库的名字。具体如下:

  虽然这样可以使用静态库,但是不支持这种做法。这样就有可能会污染系统的文件。

3、2、2 指定路径直接使用

  加载到系统的文件中不是一种很优的方法。哪还有其他方法吗?答案有的。我们在编译时直接告诉编译器路径:gcc main.c -I ./hello/include/ -L ./hello/lib/ -lhello。具体如下图:

3、3 动态库制作

   我们知道,静态库在编译过程中,目标文件被编译链接生成的可执行程序中包含所有函数和数据。可执行程序所占用内存比较大。当程序运行时,所有的代码和数据都被加载到内存中,并占用固定的内存地址。这种方式是静态链接,使得可执行文件的体积较大,且不具备代码共享的特性。而动态库并不是这样的,且动态库的生成与静态库也有所区别。我们接着往下看。

  动态库(Dynamic Link Library,简称DLL)是一种可执行文件,包含可以被多个程序同时调用的函数和数据。与静态库相比,动态库提供了更为灵活和高效的代码共享方式。

形成动态库的过程主要包括以下几个步骤:

  1. 编写动态库代码:根据需求编写所需的函数和数据,并将其封装在一个动态链接库项目中。

  2. 编译动态库代码:使用合适的编译器将动态库代码编译成二进制形式,生成具有扩展名为.dll(在Windows系统中)或.so(在Linux系统中)的文件。

  3. 链接动态库:将动态库文件与主程序进行链接,使主程序能够使用库中的函数和数据。在这个过程中,主程序并不会把动态库的内容复制到自己的代码中,而是在运行时通过动态链接来加载和使用库中的函数和数据。

  通过上图我们发现,在生成.o文件时多出了-fPIC选项。-fPIC选项是什么意思呢?

  gcc的-fPIC选项是用于生成可重定位目标文件(Position Independent Code,PIC)的编译选项。通过使用该选项,生成的目标文件可以在内存中的任何位置加载和执行,而不需要进行修改或重新链接

  在编译过程中,目标文件只包含程序所需的函数和数据的引用信息,真正的函数和数据则通过动态链接库(Dynamic Linking Library,DLL)提供。在程序运行时,操作系统会将需要的函数和数据从动态链接库中加载到内存,并进行地址重定向。这种方式使得可执行文件的体积较小,且不同程序之间可以共享同一个动态链接库。

  而-fPIC选项则是在编译过程中产生与位置无关的代码,主要用于动态链接库的创建。使用该选项可以确保生成的目标文件能够适应不同的内存布局和地址空间。具体来说,-fPIC选项会通过使用相对寻址(relative addressing)的方式替代绝对寻址(absolute addressing),使得目标文件中的函数和数据可以在不同的内存地址加载和执行。

  当我们有了目标文件(.o)后,再看如下图生成动态库:

3、4 动态库的使用

  为了同时生成静态库和动态库,我们再次进行对makefile文件进行改写,代码如下:

.PHONY:all
all:libhello.so libhello.a

libhello.so:mymath_d.o myprint_d.o
	gcc -shared mymath_d.o myprint_d.o -o libhello.so
mymath_d.o:mymath.c
	gcc -c -fPIC mymath.c -o mymath_d.o
myprint_d.o:myprint.c
	gcc -c -fPIC myprint.c -o myprint_d.o

libhello.a: mymath.o myprint.o
	ar -rc libhello.a mymath.o myprint.o
mymath.o:mymath.c
	gcc -c mymath.c -o mymath.o
myprint.o:myprint.c
	gcc -c myprint.c -o myprint.o

.PHONY:output
output:
	mkdir -p output/lib
	mkdir -p output/include
	cp -rf *.h output/include
	cp -rf *.a output/lib
	cp -rf *.so output/lib

.PHONY:clean
clean:
	rm -rf *.o *.a *.so output

3、4、1 指定路径直接使用

   我们同样是先把打包动静态库和头文件目录(hello)拷贝到上级目录下一个文件中进行调用使用。动态库的直接指定路径使用与静态库相似。我们看如下实例:

  我们知道,动态库和静态库真是的名字去掉前后缀后都是hello。那我们直接使用-lhello,指定的是动态库还是静态库呢?也就是默认情况下是动态链接呢?还是静态链接呢?我们看选图:

  我们发现,默认情况下是动态链接。但是并没有找到动态库!且生成的可执行程序也不能执行。这又是为什么呢?原因是我们需要把动态库加载到内存中后,可被多个进程使用(因此也被称为共享库)。但是我们只是告诉gcc动态库所在的路径了,并没有告诉操作系统动态库在哪里!

3、4、2 环境变量:LD_LIBRARY_PATH 

  在Linux下,默认查找共享库的环境变量是LD_LIBRARY_PATHLD_LIBRARY_PATH是一个包含目录路径的环境变量,用于告诉动态链接器(ld.so)在哪些目录中搜索共享库文件

当程序需要加载共享库时,动态链接器会按照以下顺序进行搜索:

  1. 优先搜索程序内部指定的路径。
  2. 如果没有找到,接下来会搜索LD_LIBRARY_PATH中指定的路径。
  3. 如果还是没有找到,最后动态链接器会按照一定的默认规则搜索系统预定义的路径,如/usr/lib/lib等。

  通过设置LD_LIBRARY_PATH环境变量,可以临时修改共享库的搜索路径。例如,可以使用以下命令来设置LD_LIBRARY_PATH环境变量:

export LD_LIBRARY_PATH=/path/to/shared/libs

  该命令将/shared/libs目录添加到共享库搜索路径中。在当前的终端会话中,程序运行时将会优先搜索该路径下的共享库。

  但是LD_LIBRARY_PATH是一个临时的环境变量设置,只对当前终端会话有效。如果希望永久修改共享库的搜索路径,可以考虑修改系统范围内的配置文件,如/etc/ld.so.conf.d/目录下的配置文件,并执行相应的更新操作,例如使用ldconfig命令。稍后我们也会详细介绍。

  现在以我自己为例子来添加动态库到共享库的环境变量中。具体如下图:

  其实我们也不难发现,每个路径都是用 :来进行分割的。我们添加成功后,我们再次执行a.out 时,系统就回根据环境变量自动找到改动挑库所在的位置。运行如下图:

3、4、3 系统文件:/etc/ld.so.conf.d/ 

  在Linux系统中,/etc/ld.so.conf.d/目录是用来配置共享库搜索路径的。共享库在运行时被程序动态链接使用,这些库存储在特定的路径下。ld.so是动态链接器(loader)的一部分,它负责在运行程序时定位和加载所需的共享库。

  在/etc/ld.so.conf.d/目录中,可以创建不同的配置文件,每个文件对应一个共享库搜索路径。这些配置文件通常以.conf为后缀名。通过在这些配置文件中添加共享库的路径,可以告诉动态链接器在特定的目录中搜索共享库

  当系统启动或需要加载共享库时,动态链接器会读取这些配置文件,并根据其中的路径进行查找。如果某个共享库存在于指定的路径中,那么它将被加载到内存中供程序使用。使用/etc/ld.so.conf.d/目录可以方便地管理共享库路径的配置。可以在不同的配置文件中分别设置不同的共享库路径,这样可以根据需要独立地管理和更新路径的配置,而不会影响到其他配置文件。

  我们再来看一下具体的操作实例。我们在 /etc/ld.so.conf.d/ 下创建一个test.conf 文件。然后把我们动态库所在的路径编辑加入test.conf 文件中。

  我们再看就可以找到动态库了。

 四、总结

  动态库和静态库是两种代码库的形式,它们主要用于程序的模块化开发和代码共享。它们具有以下不同之处:

  1. 静态库(Static Library): 静态库在编译时会被完全链接到可执行文件中,使得可执行文件包含了所有需要的库函数和数据。使用静态库的主要优点是程序的独立性,无需依赖外部库文件即可运行。静态库适用于一些较小的应用,或者需要独立部署的情况。

  2. 动态库(Dynamic Library): 动态库在程序运行时由操作系统进行加载和链接,而不是在编译时完全链接到可执行文件。程序在运行时只需要动态库的引用并调用相应函数即可。使用动态库的主要优点是节约系统资源和提高可维护性。多个程序可以共享同一个动态库,减少了内存占用和可执行文件的大小。

为什么要有动态库和静态库呢?主要原因包括:

  1. 代码共享:将常用的功能进行封装成库,可以被多个应用程序共享使用,避免重复编写相同的代码,提高了代码的复用性和开发效率。

  2. 系统资源的优化:动态库的方式可以在程序运行时动态加载和链接,节省了内存的占用空间,提高了系统资源的利用效率。

  3. 可维护性:使用库的方式可以使得程序模块化,便于维护和更新。当库需要升级或修复BUG时,只需替换库文件而无需修改引用该库的程序。

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

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

相关文章

RenderDoc 导出Cubemap到UE

找到使用了Cubemap的模型,再Output里会显示该模型使用的所有贴图 ,选中Cubemap导出 选择导出格式为HDR 导出的Cubemap是竖着的,需要再PS里逆时针旋转90度 还有,导出的的Cubemap方向是错的,需要把3,4 跟1,2 对换,6旋转180度 UE 文档里的方向参…

CefSharp自定义缓存实现

提高页面加载加速:CefSharp缓存可以缓存已经加载过的页面和资源,当用户再次访问相同的页面时,可以直接从缓存中加载,而不需要重新下载和解析页面和资源,从而加快页面加载速度。减少网络流量:使用缓存可以减…

如何声明一个全局变量?有什么注意事项?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 声明全局变量⭐ 注意事项⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅!这个专栏是为那些对Web开发感兴趣、刚…

wxPython两个页面跳转简单方案

import wxclass Frame1(wx.Frame):def __init__(self, parent):super().__init__(parent)panel wx.Panel(self)button wx.Button(panel, label"跳转到Frame2")button.Bind(wx.EVT_BUTTON, self.on_button_click)sizer wx.BoxSizer(wx.VERTICAL)sizer.Add(button, …

大数据-玩转数据-Flink App市场推广统计

一、说明 电商网站中已经有越来越多的用户来自移动端,相比起传统浏览器的登录方式,手机APP成为了更多用户访问电商网站的首选。对于电商企业来说,一般会通过各种不同的渠道对自己的APP进行市场推广,而这些渠道的统计数据&#xf…

SpringBoot 插件化开发

SpringBoot 插件化开发 介绍使用插件的好处1 模块解耦2 提升扩展性和开放性3 方便第三方接入 插件化常用实现思路Java常用插件实现方案ServiceLoader方式1 java spi2 java spi 简单案例 自定义配置约定方式添加配置文件自定义配置文件加载类自定义测试接口启动类项目结构 自定义…

【python地理信息绘制入门】cartopy学习

cartopy学习 cartopy简介cartopy绘制中国行政地图(cartopy的版本为0.20.0)cartopy绘制中国行政地图,单一省细分画出市区cartopy库的一些问题 cartopy简介 cartopy是一个用于绘制地图投影和地理数据可视化的 Python 库。它是建立在 matplotli…

约瑟夫占位问题

面试题中的考查: 其实这个就是一个约瑟夫问题,下面通过讲解你会很好的解决这道题目。 故事背景: 据说在罗马帝国时期,一群犹太士兵被罗马人包围,为了不当罗马人的俘虏,犹太士兵们决定集体自杀,…

java实现人物关系抽取

java实现人物关系抽取 人物关系抽取是实体关系抽取的一种情况。实际上是两个过程:命名实体识别和关系抽取。 Java人物关系抽取是指从文本中提取出与Java相关的人物之间的关系。这个过程可以通过自然语言处理和文本分析的方法来实现。具体的步骤包括: 文本…

PCTA 认证考试高分通过经验分享

作者: msx-yzu 原文来源: https://tidb.net/blog/0b343c9f 序言 我在2023年8月10日,参加了 PingCAP 认证 TiDB 数据库专员 V6 考试 ,并以 90分 的成绩通过考试。 考试总分是100分,超过60分就算通过考试。试卷…

深入理解Linux内核--访问文件

访问文件模式 访问文件的模式有多种。我们在本章考虑如下几种情况:规范模式规范模式下文件打开后,标志O_SYNC与0_DIRECT清0,而且它的内容是由系统调用read()和write()来存取。系统调用read()将阻塞调用进程,直到数据被拷贝进用户态地址空间(…

C#与西门子PLC1500的ModbusTcp服务器通信2--ModbusTcp协议

Modbus TCP是近年来越来越流行的工业控制系统通信协议之一,与其他通信协议相比,Modbus TCP通信速度快、可靠性高、兼容性强、适用于模拟或数字量信号的传输,阅读本文前你必须比较熟悉Modbus协议,了解tcp网络。 一、什么是Modbus …

win10 wsl ubuntu 更换版本为18.04 apt换国内源Python换国内源;默认root; Ubuntu18.04 下载与安装(阿里云官方镜像站)

控制面板里面应用模块找到Ubuntu,可以卸载或者移动到其他盘。 Microsoft 应用程序 - ubuntu https://apps.microsoft.com/store/search/ubuntu?hlzh-cn&glcn&rtc1 选择想要的版本安装。 cp /etc/apt/sources.list /etc/apt/sources.list.bak nano /etc/ap…

Java程序设计——拼图游戏

建立四个类 LeftjPanel、LoginPintu、MainJFranme、RightJPanel 在准备一个图片 LeftjPane类 import java.awt.Image; import java.net.MalformedURLException; import java.net.URL;import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JPanel;…

中国电信秋招攻略,考试内容分析

电信秋招简介 每年的毕业生人数都在逐年递增,逐年递增就意味着竞争会越来越大,最好比别人做更充足的准备。要确定好就业方向以及就业的岗位,要了解各种各样的流程,做好一切自己能做到的准备。而对于有想法进入电信公司工作的人来…

第 7 章 排序算法(1)(介绍,分类,时间复杂度,空间复杂度)

7.1排序算法的介绍 排序也称排序算法(Sort Algorithm),排序是将一组数据,依指定的顺序进行排列的过程。 7.2排序的分类: 内部排序: 指将需要处理的所有数据都加载到**内部存储器(内存)**中进行排序。外部排序法: 数据量过大&am…

【Unity每日一记】SceneManager场景资源动态加载

👨‍💻个人主页:元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 收录于专栏:uni…

Java程序设计——编写国际象棋棋盘(5*6的黑白方格)

重点代码: 调整黑白方格 if(i%20) { if(k%20) j[i].setBackground(color[0]); else j[i].setBackground(color[1]); add(j[i]); } else …

spring boot分装通用的查询+分页接口

背景 在用spring bootmybatis plus实现增删改查的时候,总是免不了各种模糊查询和分页的查询。每个数据表设计一个模糊分页,这样代码就造成了冗余,且对自身的技能提升没有帮助。那么有没有办法实现一个通用的增删改查的方法呢?今天…

分享8个新鲜的 VSCode 插件,提高你的开发生产效率

Visual Studio Code通常被称为VSCode,是一款开源、轻量但功能强大的源代码编辑器。被全球开发者广泛使用,它提供了丰富的扩展生态系统,适用于各种类型的开发者,增强了用户在多种语言中编码、高效调试甚至在编码过程中引入一些乐趣…