Linux中的动静态库

news2024/12/28 20:26:17

目录

前言

1.库的文件名

2.库的制作

对于动态库: 

对于静态库:

 3.库文件的查找

4.库的加载与使用

对于动态库:

对于静态库:

可执行程序分段:

程序的编址于动态库:

总结


前言

在刚开始学习C语言代码的时候,我们需要引入一个名为stdio.h的头文件,在头文件中为我们一些读写方法的声明,我们可以根据这些声明来使用接口实现对应的功能。但我们要知道,光有函数方法的声明是无法正常使用一个函数,还需要函数的具体定义,函数的定义就写在对应的库文件中。在本文中我们将介绍如何制作一个库,以及动静态库的区别。

1.库的文件名

在Linux中,无论是动态库还是静态库其文件名的前缀都是lib而后紧跟库的名称,最后用.so/.a表示该库是动态库还是静态库

lib[库名字].so        //动态库

lib[库名字].a          //静态库

图1        Linux中的一些文件

举个例子,对于第一行中红色方框框住的库是一个动态库,库的名字是atomic

对于第二行中红色方框框住的库是一个静态库,库的名字是gcov

2.库的制作

//生成一个静态库
ar -rc [生成库的文件名] [依赖的文件]                  

//生成一个动态库
gcc -c -fPIC [依赖文件的源文件]            //将库源文件编译成.o文件
gcc -shared [生成库的文件名] [依赖的文件]   //链接库文件与自己编写的主函数文件

说明:

-fPIC选项:生成位置无关代码,位置无关是指当程序引用添加该选项生成的库文件时,不会因为该库文件加载到内存中的位置不同而影响库的功能。这使得多个程序可以同时引用该库,且平台可移植性更好

-shared选项:使用该选项生成的库文件有着即时更改的性质,意思是当库升级或由其他变动的时候无需将使用该库的程序重新编译,只需要将库重新生成即可。这样可以减少因库中有错误而重新编译生成可执行文件的时间。

简单实现两个数的加减操作的库:

这里分四个文件来进行演示,分别是Add函数定义的C++文件、Sub函数定义的C++文件、包含Add与Sub函数声明的头文件、包含主函数的文件。

图2        方法相关代码

对于动态库: 

 在制作库前,先复习这样一个问题:如何编译生成一个可执行文件。

首先我们要去掉注释,展开头文件、编译源文件生成.o文件、链接编译后的相关.o文件。也就是说我们使用的库其实也需要和我们写的程序一同完成上述的这些过程,但是在我们的本地IDE上所有的库本质上都是一个个完成编译后的.o文件集成的包,所以当我们自己写完的程序完成编译后,链接器直接链接链接库和我们的程序对象文件(.o文件)即可。

所以,如果我们想制作一个动态库,在完成对库源文件的编辑后,就必须要将我们自己写的库先编译成.o文件,然后与我们的主函数源文件一同编译链接即可。

图3        不打包成库如何统合各文件

图4        打包成库如何统合各文件

说明:

图5        链接中各选项的意义

对于静态库:

相比于动态库,静态库的制作显得更加简便,只需要使用ar工具进行编译打包即可,具体操作如图6上半部分所展示。

值得一提的是:

当库的名字一样时,当动静态库同时存在时,链接器会优先使用动态库,具体验证如图6。在图6上半部分中,只有静态库,那么此时链接器链接静态库,链接后的可执行程序文件属性在终端中显示。在图6下半,让动静态库同时存在,此时再次链接库,此时链接器使用动态库链接生成可执行文件,使用ldd命令可以查看可执行程序链接的动态库,在图6下半中,可以看到所连接的库名称。使用file命令可以查看可执行文件的链接方式。可以看到两个文件都是动态连接的。

当使用gcc命令编译链接程序入口文件时,可以添加“-static”选项,让链接器使用静态链接的方式链接库,此时优先链接静态库。如果不加该选项,那么链接器就是用动态链接的方式,优先链接动态库,若在存在动态库的前提下,选择链接静态库,那么会发生报错。

(以上结论基于Ubuntu20.04)

图6        使用动静态库链接入口对象文件对比图

3.库文件的查找

在第二部分中,编译链接一个库的选项过于复杂,为什么我们使用语言的库就不需要这么大费周章的显示路径,告诉链接器要链接的库文件名呢?

图7        链接时的选项

这是因为,链接链接文件时会从系统的头文件默认保存下查找所需的头文件,而后从系统的库文件默认保存路径下查找库,我们自己写的库的头文件与库文件都不在这个对应的路径下,所以需要为链接器指明。此外,由于我们自己编写的库都会被认为成是一个外部库,对于链接外部库的可执行文件必须要指明链接库的名称。 

通常情况下我们有四种方式来简化我们的链接指令:

①将我们自己编写的外部库放到系统存放库的文件夹中

②将我们自己编写外部库的路径设置进环境变量中

③利用软硬链接生成库入口文件,将该文件设置到系统存放库的文件夹中

④将自己编写的库路径信息内置到配置文件中 

实现①:

在Ubuntu20.04下,存放库文件的路径是/lib,存放库文件的头文件的路径是/usr/include。在库路径下放置我们的库文件,在头文件目录下放置头文件。

图8        将自己实现的库安装到系统路径下

实现②:

图9        将库路径设置进环境变量

 如果没有对应的LD_LIBRART_PATH环境变量则自己创建一个,如图9写入环境变量是内存级写入,虚拟机或云服务器关机后将会被恢复,如果想不被恢复就必须将环境变量设置进文件。

关于如何创建一个环境变量并将其设置进文件,请参考【Linux中的环境变量】

实现③:

图10        使用软连接访问库

关于软硬链接有需要的读者可以阅读【Linux文件系统(下)】 

实现④: 

图11        将库路径配置进文件      

4.库的加载与使用

对于动态库:

首先我们要明确一点的是,库也是文件,那么既然是文件就需要遵守文件系统的管理规则。库在没有被加载时会像其他文件一样被保存在磁盘上,(假设文件系统是ext系列文件系统),文件系统会为库分配内存块,inode表等。当库被链接,也就是库有读需求的时候,操作系统查看该库与inode的绑定关系,而后向磁盘发出访问申请,申请成功后,磁盘中的数据将会被加载到物理内存中,在物理内存中数据将以指针的指向的形式被指向,之后与进程地址空间中的共享区填充页表映关系。

 这样库就可以被进程使用了,在该进程中的可执行性程序就可以对该库进行链接使用。

说明:

动态库与普通文件加载的不同之处:

①加载目的:

动态库:通常是要被多个文件进行只读使用的,因此动态库在底层设计时就被允许了多文件的访问与链接,来减少库在内存中重复加载,提高了内存的利用效率。

普通文件:通常是为了特定程序使用的,而且普通文件的读写需求是不确定的。因此多个程序对同一文件有读写需求就需要拷贝多个副本。

②加载方式:

动态库:通常需要操作系统使用对应的接口,将加载到内存中的库进行符号解析、代码重定位等操作。

普通文件:则不需要进行上述操作

③内存管理:

动态库:在一个进程中往往会有加载多个库,操作系统要对这些加载的库进行管理,记录这些库的使用情况。

普通文件:在内存中的管理往往都是由他所对应的程序完成的

④文件更新:

动态库:被更改时,正在运行的程序可能会因库中接口或库中逻辑的变化受到影响而发生崩溃。

普通文件:通常在内存中由一个副本,程序对文件的操作都是对副本的操作,只要不强制更新的文件文件副本与磁盘上的文件内容同步,程序仍然可以使用更新后的文件。

图12        库加载过程示意图

对于静态库:

静态库在编译链接后直接拷贝在程序的内部中,不同于动态库复杂的加载过程,它随着程序一同从磁盘中加载。这里就不过多介绍了。

可执行程序分段:

size [可执行程序名]

使用上述命令可以查看可执行程序各个段的大小 

图13        一可执行程序内存分布

text(代码段):用来存储代码的区域,通常为只读状态。

data(数据段):用来存储已初始化的全局变量和静态变量。

bss:用来存储未初始化的全局变量和静态变量,这些变量在程序加载到内存时,会自动初始化为0。

dec、hex:分别是前三个段总大小的十进制表示和十六进制表示。

将程序中的数据进行分段存储可以提高程序的运行效率:

比如,当一个数据量庞大的程序要运行时,为了程序尽可能快的开始运行,操作系统会将先用到的数据加载到内存,暂时未用到的数据延后加载。

 此外,代码分段还可以更好的提高程序的安全性:

比如,如果有对代码段进行改写的行为,那么这种行为将会被禁止,此外还可以对特定段进行加密,进而提升程序的安全性。

程序的编址于动态库:

对于任何可执行程序,实际上都已经被“地址化”了。

图14        一程序反汇编后文件内容

将可执行程序反汇编,可以看到,我们的程序中的每一条指令都被“地址化”,在可执行程序中,没有变量,只有寄存器和地址,当CPU执行我们的程序时,实际上就是至上而下的遍历地址并更改寄存器。对于动态库,库的内容会加载到该可执行程序所在的进程地址空间中的共享区,在反汇编文件中,我们可以得到每一个函数调用的地址,当程序要调用库中定义的函数时,CPU会在寄存器的帮助下,跳转到共享区中对应的部分,当CPU执行完函数体内的内容后,又会在寄存器的帮助下跳转回导致跳转的下一条指令所在的地址。

图15        函数跳转示意图

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

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

相关文章

NASA:ASTER L1A 重建未处理仪器数据 V003

ASTER L1A 重建未处理仪器数据 V003 简介 先进星载热发射和反射辐射计(ASTER)1A 级(AST_L1A)包含重建的仪器数字编号(DN),这些数字编号来自所获取的望远镜遥测数据流: 可见光和近红…

综合题第一题(地址表的填写)

题目 第一题的形式大概就是这样的,通常IP地址和子网掩码会给我们。 地址类别 补充知识 IP地址(Internet Protocol Address)是分配给网络中设备的数字标签,用于标识设备在网络中的位置。IP地址分为IPv4和IPv6两种版本&#xff0…

用Qt 对接‌百度AI平台

很多同学想利用几大模型AI弄点东西,但又不知道如何去介入??最近帮同学弄点东西,刚好要接入到AI平台,就顺便研究了一下,并记录下来。 首先我们选择的 AI模型是百度的,然后注册,申请密…

vue实现数据栏无缝滚动实现方式-demo

效果 方式一 通过实现两个item 进行循环 <!--* Author: Jackie* Date: 2023-08-16 21:27:42* LastEditTime: 2023-08-16 21:41:51* LastEditors: Jackie* Description: scroll 水平滚动 - 效果基本满足需求* FilePath: /vue3-swiper-demo/src/components/scroll/Scroll12.…

开始场景的制作+气泡特效的添加

3D场景或2D场景的切换 1.新建项目时选择3D项目或2D项目 2.如下图操作&#xff1a; 开始前的固有流程 按照如下步骤进行操作&#xff0c;于步骤3中更改Company Name等属性&#xff1a; 本案例分辨率可以如下设置&#xff0c;有能力者可根据需要自行调整&#xff1a; 场景制作…

python是什么语言写的

Python是一种计算机程序设计语言。是一种面向对象的动态类型语言。现今Python语言很火&#xff0c;可有人提问&#xff0c;这么火的语言它的底层又是什么语言编写的呢&#xff1f; python是C语言编写的&#xff0c;它有很多包也是用C语言写的。 所以说&#xff0c;C语言还是很…

算法.图论-并查集

文章目录 1. 并查集介绍2. 并查集的实现2.1 实现逻辑2.2 isSameSet方法2.3 union方法(小挂大优化)2.4 find方法(路径压缩优化) 3. 并查集模板4. 并查集习题4.1 情侣牵手4.2 相似字符串组 1. 并查集介绍 定义&#xff1a; 并查集是一种树型的数据结构&#xff0c;用于处理一些不…

(学习记录)使用 STM32CubeMX——配置时钟(入门)

使用STM32CubeMX配置STM32F103C8T6时钟部分 选择芯片 ①&#xff1a;选择MCU型号 ①&#xff1a;这里使用英文输入法&#xff0c;输入你想要的芯片型号&#xff0c;我这里采用STM32F103C8T6 ②&#xff1a;这里能看到搜索后出来的芯片具体型号&#xff0c;选择匹配度最高的一个…

类和对象(2)(重点)

个人主页&#xff1a;Jason_from_China-CSDN博客 所属栏目&#xff1a;C系统性学习_Jason_from_China的博客-CSDN博客 所属栏目&#xff1a;C知识点的补充_Jason_from_China的博客-CSDN博客 类的默认成员函数 概念概述 默认成员函数就是用户没有显式实现&#xff0c;编译器会自…

【CSS in Depth 2 精译_034】5.4 Grid 网格布局的显式网格与隐式网格(下)

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一章 层叠、优先级与继承&#xff08;已完结&#xff09; 1.1 层叠1.2 继承1.3 特殊值1.4 简写属性1.5 CSS 渐进式增强技术1.6 本章小结 第二章 相对单位&#xff08;已完结&#xff09; 2.1 相对…

ACM MM24 | Hi3D: 3D生成领域再突破!新视角生成和高分辨率生成双SOTA(复旦智象等)

文章链接&#xff1a;https://arxiv.org/pdf/2409.07452 Github 链接&#xff1a;https://github.com/yanghb22-fdu/Hi3D-Official 亮点直击 本文提出了高分辨率图像到3D模型&#xff08;Hi3D&#xff09;&#xff0c;这是一种基于视频扩散的新范式&#xff0c;将单个图像重新定…

计算机毕业设计python+spark知识图谱房价预测系统 房源推荐系统 房源数据分析 房源可视化 房源大数据大屏 大数据毕业设计 机器学习

《PythonSpark知识图谱房价预测系统》开题报告 一、研究背景与意义 随着城市化进程的加速和房地产市场的不断发展&#xff0c;房价成为影响人们生活质量的重要因素之一。准确预测房价不仅有助于政府制定科学的房地产政策&#xff0c;还能为开发商提供市场参考&#xff0c;同时…

NLP-transformer学习:(7)evaluate实践

NLP-transformer学习&#xff1a;&#xff08;7&#xff09;evaluate 使用方法 打好基础&#xff0c;为了后面学习走得更远。 本章节是单独的 NLP-transformer学习 章节&#xff0c;主要实践了evaluate。同时&#xff0c;最近将学习代码传到&#xff1a;https://github.com/Mex…

c++类与对象一

C类与对象(一) 面向对象初步认识 在c语言中&#xff0c;编程是面向过程编程&#xff0c;注重求解问题列出过程&#xff0c;然后调用函数求解问题。 在日常生活中。我们经常会遇到面向过程的问题 手洗衣服就是面向过程 而C是基于面向对象的。关注的是对象&#xff0c;把事情…

SpringSecurity -- 入门使用

文章目录 什么是 SpringSesurity &#xff1f;细节使用方法 什么是 SpringSesurity &#xff1f; 在我们的开发中&#xff0c;安全还是有些必要的 用 拦截器 和 过滤器 写代码还是比较麻烦。 SpringSecurity 是 SpringBoot 的底层安全默认选型。一般我们需要认证和授权&#xf…

【Finetune】(三)、transformers之P-Tuning微调

文章目录 0、P-Tuning基本原理1、代码实战1.1、导包1.2、加载数据集1.3、数据集预处理1.4、创建模型1.5、P-tuning*1.5.1、配置文件1.5.2、创建模型 1.6、配置训练参数1.7、创建训练器1.8、模型训练1.9、模型推理 0、P-Tuning基本原理 P-Tuning的基本思想是在prompt-tuning的基…

Spring Boot管理用户数据

目录 学习目标前言Thymeleaf 模板JSON 数据步骤 1: 创建 Spring Boot 项目使用 Spring Initializr 创建项目使用 IDE 创建项目 步骤 2: 添加依赖步骤 3: 创建 Controller步骤 4: 新建index页面步骤 5: 运行应用程序 表单提交步骤 1: 添加 Thymeleaf 依赖在 Maven 中添加依赖 步…

Vue3.3新特性defineModel

defineModel的使用: defineModel选项可以帮我们省去很多麻烦 不仅需要上述操作&#xff0c;还需要进行一定的配置&#xff1a; 在vite.config.js中进行配置 defineModel是一个宏&#xff0c;所以不需要从vue中import导入&#xff0c;直接使用就可以了。这个宏可以用来声明一个…

Java之线程篇六

目录 CAS CAS伪代码 CAS的应用 实现原子类 实现自旋锁 CAS的ABA问题 ABA问题导致BUG的例子 相关面试题 synchronized原理 synchronized特性 加锁过程 相关面试题 Callable 相关面试题 JUC的常见类 ReentrantLock ReentrantLock 和 synchronized 的区别: 原…

Android 新增目录怎么加入git

工作中会遇到android系统源码系统增加第三方功能支持&#xff0c;需要增加目录&#xff0c;那么这个目录怎么提交到服务器上去呢&#xff1f;接下来我们就看下这个问题的解决 一服务器创建仓库 一个新的目录增加到仓库中&#xff0c;需要仓库有对应的仓库地址&#xff0c;这个让…