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

  

  本篇文章主要是围绕动静态库的原理与制作进行展开讲解的。其中涉及到了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.oar -rc libhello.a mymath.o myprint.omymath.o:mymath.cgcc -c mymath.c -o mymath.omyprint.o:myprint.cgcc -c myprint.c -o myprint.o.PHONY:hello
hello:mkdir -p hello/libmkdir -p hello/includecp -rf *.h hello/includecp -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.alibhello.so:mymath_d.o myprint_d.ogcc -shared mymath_d.o myprint_d.o -o libhello.so
mymath_d.o:mymath.cgcc -c -fPIC mymath.c -o mymath_d.o
myprint_d.o:myprint.cgcc -c -fPIC myprint.c -o myprint_d.olibhello.a: mymath.o myprint.oar -rc libhello.a mymath.o myprint.o
mymath.o:mymath.cgcc -c mymath.c -o mymath.o
myprint.o:myprint.cgcc -c myprint.c -o myprint.o.PHONY:output
output:mkdir -p output/libmkdir -p output/includecp -rf *.h output/includecp -rf *.a output/libcp -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.mzph.cn/news/44773.shtml

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

相关文章

编织梦想:SpringBoot AOP 教程与自定义日志切面完整实战

什么是 AOP AOP 是指通过预编译方式和运行期动态代理的方式,在不修改源代码的情况下对程序进行功能增强的一种技术。AOP 不是面向对象编程(OOP)的替代品,而是 OOP 的补充和扩展。它是一个新的维度,用来表达横切问题&a…

常见前端基础面试题(HTML,CSS,JS)(三)

JS 中如何进行数据类型的转换? 类型转换可以分为两种,隐性转换和显性转换 显性转换 主要分为三大类:数值类型、字符串类型、布尔类型 三大类的原始类型值的转换规则我就不一一列举了 数值类型(引用类型转换) Numbe…

设计模式之状态模式(State)的C++实现

1、状态模式的提出 在组件功能开发过程中,某些对象的状态经常面临变化,不同的状态,其对象的操作行为不同。比如根据状态写的if else条件情况,且这种条件变化是经常变化的,这样的代码不易维护。可以使用状态模式解决这…

如何在window下cmd窗口执行linux指令?

1.Git:https://git-scm.com/downloads(官网地址) 2.根据自己的实际路径,添加两个环境变量 3.重启电脑

删除有序链表中重复的元素-II(链表)

乌!蒙!山!连!着!山!外!山! 题目: 思路: 双指针,slow和fast,并且增加标记flag初始为1。 如果slow指向节点值等于fast指向节点值&…

Servlet 初步学习

文章目录 Servlet1 简介2 快速入门3 执行流程4 生命周期5 方法介绍6 体系结构7 urlPattern配置8 XML配置 Servlet 1 简介 Servlet是JavaWeb最为核心的内容,它是Java提供的一门 动态 web资源开发技术。 使用Servlet就可以实现,根据不同的登录用户在页面…

什么是cURL?

cURL无处不在。它几乎隐藏在所有设备中,例如汽车,蓝光播放器等。它通过互联网协议传输任意类型数据。 在本文中,我们将揭开cURL神秘命令行工具的面纱,解释它是如何成为一种通用代码的,并举例说明其用法。 cURL是什么意…

PHP海外代购管理系统mysql数据库web结构apache计算机软件工程网页wamp

一、源码特点 PHP 海外代购管理系统是一套完善的web设计系统,对理解php编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。 代码下载 https://download.csdn.net/download/qq_41221322/88229435 论文 https://…

Redis中的分布式锁及其延生的问题

前言 本文将着重介绍Redis中的分布式锁及其与出现的死锁和锁误删问题 什么是分布式锁 首先问题就是什么是分布式锁,分布式锁就是分布式系统中实现并发控制的一种锁机制,它可以保证多个节点在同一个时间只有有一个能成功竞争到系统资源(共享…

LeetCode算法递归类—二叉树中的最大路径和

目录 124. 二叉树中的最大路径和 - 力扣(LeetCode) 题解: 代码: 运行结果: 二叉树中的 路径 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该…

基于Opencv的虚拟拖拽项目

预备知识 勾股定理 跟随移动算法 手势识别图解 项目源代码 """ 演示一个简单的虚拟拖拽 步骤: 1、opencv 读取视频流 2、在视频图像上画一个方块 3、通过mediapipe库获取手指关节坐标 4、判断手指是否在方块上 5、是,方块跟着移动 6、…

密码学学习笔记(十九):密码学关键术语的解释1

数据加密标准(DES) 数据加密标准是使用最广泛的加密体制,它于1977年被美国国家标准和技术研究所(NIST)采纳为联邦信息处理标准FIPS PUB 46。 DES3DESAES明文分组长度(位)6464128密文分组长度(位)6464128密钥长度&…

详解junit

目录 1.概述 2.断言 3.常用注解 3.1.Test 3.2.Before 3.3.After 3.4.BeforeClass 3.5.AfterClass 4.异常测试 5.超时测试 6.参数化测试 1.概述 什么是单元测试: 单元测试,是针对最小的功能单元编写测试代码,在JAVA中最小的功能单…

0101读写分离测试-jdbc-shardingsphere-中间件

文章目录 1 前言2、创建SpringBoot程序2.1、创建项目2.2、添加依赖2.3、生成实体类、service与Mapper1.5、配置读写分离 2、测试2.1、读写分离测试2.2、事务测试2.3、负载均衡测试 结语 1 前言 shardingshpere-jdbc定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的…

【前端面试】中大文件上传/下载:中等文件代理服务器放行+大文件切片传输+并发请求+localstorage实现断点续传

目录 中等文件代理服务器放行:10MB为单位 proxy nginx 大文件切片:100MB为单位 断点:存储切片hash 前端方案A localstorage 后端方案B 服务端 上传 前端 后端 下载 前端 后端 多个大文件传输:spark-md5 哈希碰撞…

什么是Pytorch?

当谈及深度学习框架时,PyTorch 是当今备受欢迎的选择之一。作为一个开源的机器学习库,PyTorch 为研究人员和开发者们提供了一个强大的工具来构建、训练以及部署各种深度学习模型。你可能会问,PyTorch 是什么,它有什么特点&#xf…

JQuery快速入门教程

1、JQuery快速入门 1.1、JQuery介绍 jQuery 是一个 JavaScript 库。所谓的库,就是一个 JS 文件,里面封装了很多预定义的函数,比如获取元素,执行隐藏、移动等,目的就 是在使用时直接调用,不需要再重复定义…

微机原理与接口技术 学习笔记(二) 存储器

文章目录 一,存储器1.1 概述1.1.1 半导体存储器的分类按制造工艺: 易失性或挥发性存储器 / 不易失性或不挥发性存储器按制造工艺: 1.1.2 半导体存储器的性能指标1.1.3 半导体存储器的一般结构及组成 1.2 随机存取存储器 RAM1.2.1 静态RAM1.2.…

UML图绘制 -- 类图

1.类图的画法 类 整体是个矩形,第一层类名,第二层属性,第三层方法。 :public- : private# : protected空格: 默认的default 对应的类写法。 public class Student {public String name;public Integer age;protected I…

机器学习知识点总结:什么是GBDT(梯度提升树)

什么是GBDT(梯度提升树) 虽然GBDT同样由许多决策树组成,但它与随机森林由许多不同。 其中之一是GBDT中的树都是回归树,树有分类有回归,区分它们的方法很简单。将苹果单纯分为好与坏的是分类树,如果能为苹果的好坏程度打个分&…