Linux静态库与动态库加载
了解库:
关于库相比大家之前肯定使用过,比如C/C++里面的标准库,STL里面的各种库,我们在调用STL里的容器时都需要使用库,那么库到底是什么呢?
库的本质就是可执行程序的"半成品"
我们先来回顾一下代码的执行流程:
1.预处理: 头文件展开,去注释,宏替换,条件编译,最终形成xxx.i的文件。
2.编译: 完成词法分析、语法分析、语义分析、符号汇总等,检查无误后将代码翻译成汇编指令,最终形成XXX.s文件。
3.汇编: 将汇编指令转换成二进制指令,最终形成xxx.o文件。
4.链接: 将生成的各个xxx.o文件进行链接,最终形成可执行程序。
现在我们有这样的场景,有test1.c,test2.c,test3.c 最终经过上述的一系列处理转化成了test1.o,test2.o,test3.o,然后将这3个文件链接形成一个可执行程序a.out:

现在我想把我写的这个功能分享给别人,让别人能够使用test*.c中的方法,但是我不想让别人看到.c中的源代码,所以我们就可以,将test*.o打包成库,然后将库和头文件(方法的使用手册),发给别人,别人拿到库和头文件,查看头文件里每个方法的使用进行使用.o里的方法。

实际上,库的本质是一堆目标文件的集合(xxx.o)的集合,里面没用main函数但存在很多课调用的方法。
认识动静态库:
我们在liunx下见一见库吧,现在我创建test1.c,编写如下程序:

这是一个非常简单的c语言程序,编译运行一下:

注意我们调用了printf函数,但我们并没有写printf函数的实现,为什么能输出结果呢?主要原因是编译器将c语言的c标准库链接进来了,c标准库里已经写好了printf的函数实现。
查看文件链接的标准库:ldd

这就是链接的c标准库,我们查一下这个文件libc.so.6:

没错,libc.so.6是一个软链接文件,我们再来查看一下这个目标文件的文件类型,使用file命令:

可以发现它是一个共享的库,简单来说就是动态库。
- 在Linux当中,以
.so为后缀的是动态库,以.a为后缀的是静态库。- 在Windows当中,以
.dll为后缀的是动态库,以.lib为后缀的是静态库。
认识了动态库,那静态库,又是什么呢?
动态库是和目标文件链接,具体怎么链接下面会讲,而静态库确不同,静态库是在编译的时候,将库中的代码直接拷贝到目标文件中,这就导致了我们最终形成的目标文件会很大,但优势在于形成了可执行程序后,该可执行程序可独立运行,不再需要库,但动态库不行,在日常我们都会使用动态库,很少使用静态库。
写一个自己的库:
在认识完库后,我们其实现在已经完全可以自己写一个库,我们简单实现一下加法:
test2.c:

test3.c:

test2.h:

test3.h:

我们先编程test* .c 形成test*.o:

使用ar命令将目标文件打包成静态库,下面我们使用ar命令的-r选项和-c选项进行打包。
-r(replace):若静态库文件当中的目标文件有更新,则用新的目标文件替换旧的目标文件。-c(create):建立静态库文件。

再打包一个静态库,改后缀即可:

还可以使用ar命令中的选项查看库中的文件的信息:
-t:列出库中的文件。-v(verbose):显示详细的信息。

当我们把库给别人使用时,只需两个文件,一个存放头文件,一个存放库,所以我将所以头文件放入include目录下,两个库放在lib下,再将这两个目录放在mylib下:

使用:
现在我们就相当于这个库的使用者,我们编写main函数来使用Add和Sub函数:

这里我们不能直接用gcc像以前一样编译main.c,因为现在gcc编译器默认只认系统提供的库,而我们需要链接的是第三方库,这里我们链接第三方库有这几种方法如下:
第一种方法我们给编译器加上一些选项(推荐):
-I:指定头文件搜索路径。-L:指定库文件搜索路径。-l:指明需要链接库文件路径下的哪一个库。
注意-l后面接库名时需要去掉库的前缀lib和库的后缀.so或者.a
gcc main.c -I ./mylib/include/ -L ./mylib/lib/ -lmyc

运行一下:

第二种方法:将头文件和库拷贝到系统默认的库路径
前提我们需要知道系统默认的头文件和库文件路径:
系统头文件: /usr/include
系统库文件: /usr/lib64
将我们的库和头文件拷贝到对应目录就行,这里不做演示,因为这种方法不是很推荐,因为我们应尽量避免对系统默认文件修改。
注意上面我打包了两个库,虽然看后缀一个静态库,一个动态库,但是其实上述的库都是静态库,linux不是单单通过后缀来判断一个库的类型,下面就来看看如何真正打包一个动态库吧。
动态库打包:
还在之前的几个目标文件:

用gcc编译时带上-fPIC选项:
-fPIC(position independent code):产生位置无关码。
gcc -fPIC -c test2.c test3.c

-fPIC作用于编译阶段,告诉编译器产生与位置无关的代码,此时产生的代码中没有绝对地址,全部都使用相对地址,从而代码可以被加载器加载到内存的任意位置都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。
打包:使用-shared选项将所有目标文件打包为动态库
与生成静态库不同的是,生成动态库时我们不必使用ar命令,我们只需使用gcc的-shared选项即可。
gcc -shared -o libmyc.so test2.o test3.o

像之前一样将头文件和动态库组合进一个文件夹:

该动态库和刚才的静态库使用方法一样:
gcc main.c -o a.out -I ./libmyc/include/ -L ./libmyc/lib/ -lmyc

需要注意的是,我们使用-I,-L,-l这三个选项都是在编译期间告诉编译器我们使用的头文件和库文件在哪里以及是谁,但是当生成的可执行程序生成后就与编译器没有关系了,此后该可执行程序运行起来后,操作系统找不到该可执行程序所依赖的动态库,我们可以使用ldd命令进行查看。

我们只是告诉了编译器头文件和动态库的位置,编程成可执行程序运行后变成进程,就和编译器无关了,就变成了一个进程,进程被操作系统管理,此时操作系统还不知道头文件和动态库的位置。
解决方法如下:
方法一: 拷贝.so文件到系统共享库路径下
cp ./libmyc/lib/libmyc.so /usr/lib64/
注意系统共享库路径,系统不同可能路径名有差异,需自行百度

这时我们再运行a.out:

方法二:更改
LD_LIBRARY_PATH
LD_LIBRARY_PATH是程序运行动态查找库时所要搜索的路径,我们只需将动态库所在的目录路径添加到LD_LIBRARY_PATH环境变量当中即可。
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/testdir/libmyc/lib
此方法在ubuntu 20.4版本已失效,故不作演示,centos的小伙伴可以自己去试一下。
方法三: 配置
/etc/ld.so.conf.d/
contos:
/etc/ld.so.conf.d/路径下存放的全部都是以.conf为后缀的配置文件,而这些配置文件当中存放的都是路径,系统会自动在/etc/ld.so.conf.d/路径下找所有配置文件里面的路径,之后就会在每个路径下查找你所需要的库。所以我们只需将动态库路径放在该配置文件中,当程序执行后,系统就会通过该配置文件找到所需的库。
先来看看/etc/ld.so.conf.d/

首先将库文件所在目录的路径存入一个以.conf为后缀的文件当中。

再将这个.conf文件放入/etc/ld.so.conf.d中:

此时a.out还是无法链接动态库的,因为配置文件只在系统刚启动的时候会更新生效给我们的系统配置好,中途修改无法马上修改,所以我们用ldconfig命令更新一下配置文件:
ldconfig
最终可运行a.out,经过我的实测,unbuntu貌似无法使用该方法,不过unbuntu还有别的方法
unbuntu:
在/etc/ld.so.conf后面直接追加新第三方动态库路径:
nano /etc/ld.so.conf

同样改完后ldconfig使配置文件生效。最后运行a.out

相关文章:
Linux静态库与动态库加载
了解库: 关于库相比大家之前肯定使用过,比如C/C里面的标准库,STL里面的各种库,我们在调用STL里的容器时都需要使用库,那么库到底是什么呢? 库的本质就是可执行程序的"半成品" 我们先来回顾一下代…...
Whisper-AT:抗噪语音识别模型(Whisper)实现通用音频事件标记(Audio Tagger)
本文介绍一个统一音频标记(Audio Tagger)和语音识别(ASR)的模型:Whisper-AT,通过冻结Whisper的主干,并在其之上训练一个轻量级的音频标记模型。Whisper-AT在额外计算成本不到1%的情况下…...
K8s:Pod初识
Pod Pod是k8s处理的最基本单元。容器本身不会直接分配到主机上,封装为Pod对象,是由一个或多个关系紧密的容器构成。她们共享 IPC、Network、和UTS namespace pod的特征 包含多个共享IPC、Network和UTC namespace的容器,可直接通过loaclhos…...
HCIP-Datacom-ARST自选题库__MAC【14道题】
一、单选题 1.缺省情况下,以下哪种安全MAC地址类型在设备重启后表项会丢失? 黑洞MAC地址 Sticky MAC地址 安全动态MAC地址 安全静态MAC地址 2.华为交换机MAC地址表中的动态sticky MAC地址的默认老化时间是多少秒? 300 不会老化 400 500 3.华为交换机MA…...
Go基础编程 - 03 - init函数、main函数、_(下划线)
目录 1. init 函数2. main 函数3. init 函数与 main 函数异同4. _ (下划线)示例 1. init 函数 Go语言中,init 函数用于包(package)的初始化。具有以下特征: 1. init 函数用于程序执行前包的初始化,如初始化变量等。2…...
【TensorFlow深度学习】LeNet-5卷积神经网络实战分析
LeNet-5卷积神经网络实战分析 LeNet-5卷积神经网络实战分析:从经典模型到现代实践LeNet-5的历史背景LeNet-5网络架构实战代码解析实战分析结论 LeNet-5卷积神经网络实战分析:从经典模型到现代实践 在深度学习的历程中,LeNet-5无疑是一座里程…...
错误发生在尝试创建一个基于有限元方法的功能空间时
问题: index cell.index(#直接使用从0开始的索引if0<1ndex<10: #正集流体 subdomains_x[cell,index(] 1 fem1 /usr/bin/python3.8 /home/wy/PycharmProjects/pythonProject2/fem1.pyUnknown ufl object type FiniteElementTraceback (aost recent call last)…...
【八股】Hibernate和JPA:理解它们的关系
在Java开发中,持久化框架是至关重要的工具,它们帮助开发者将Java对象与关系数据库中的数据进行映射和管理。Hibernate和JPA(Java Persistence API)是两个广泛使用的持久化框架。那么,Hibernate和JPA之间到底是什么关系…...
C++类型参数技术以及常见的类型擦除容器
文章目录 一、类型擦除的作用二、常见的类型擦除容器1.std::any2.std::function3.std::shared_ptr\<void\>和 std::unique_ptr\<void\>4.总结 三、实现一个any参考 类型擦除(Type Erasure)是一种编程技术,通过它可以在运行时存储…...
SpringBoot如何缓存方法返回值?
Why? 为什么要对方法的返回值进行缓存呢? 简单来说是为了提升后端程序的性能和提高前端程序的访问速度。减小对db和后端应用程序的压力。 一般而言,缓存的内容都是不经常变化的,或者轻微变化对于前端应用程序是可以容忍的。 否…...
C#的web项目ASP.NET
添加实体类和控制器类 using System; using System.Collections.Generic; using System.Linq; using System.Web;namespace WebApplication1.Models {public class Company{public string companyCode { get; set; }public string companyName { get; set; }public string com…...
Spring MVC 源码分析之 DispatcherServlet#getHandlerAdapter 方法
前言: 前面我们分析了 Spring MVC 的工作流程源码,其核心是 DispatcherServlet#doDispatch 方法,我们前面分析了获取 Handler 的方法 DispatcherServlet#getHandler 方法,本篇我们重点分析一下获取当前请求的适配器 HandlerAdapt…...
假设检验学习笔记
1. 假设检验的基本概念 1.1. 原假设(零假设) 对总体的分布所作的假设用表示,并称为原假设或零假设 在总体分布类型已知的情况下,仅仅涉及总体分布中未知参数的统计假设,称为参数假设 在总体分布类型未知的情况下&#…...
vue3 watch学习
watch的侦听数据源类型 watch的第一个参数为侦听数据源,有4种"数据源": ref(包括计算属性) reactive(响应式对象) getter函数 多个数据源组成的数组。 //ref const xref(0)//单个ref watch(x,(newX)>{console.…...
推荐的Pytest插件
推荐的Pytest插件 Pytest的插件生态系统非常丰富,以下是一些特别推荐的Pytest插件: pytest-sugar 这个插件改进了Pytest的默认输出,添加了进度条,并立即显示失败的测试。它不需要额外配置,只需安装即可享受更漂亮、更…...
C语言 | Leetcode C语言题解之第124题二叉树中的最大路径和
题目: 题解: /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/ int max; int dfs(struct TreeNode* root){if(!root) return 0;int left dfs(root->left…...
Linux综合实践(Ubuntu)
目录 一、配置任务 1.1 配置该服务器的软件源为中科大软件源 1.2 安装相关软件openssh-server和vim 1.3 设置双网卡,网卡1为NAT模式,网卡2为桥接模式(桥接模式下,使用静态ip,该网卡数据跟实验室主机网络设置相似,除…...
C++面试题其二
19. STL中unordered_map和map的区别 unordered_map 和 map 都是C标准库中的关联容器,但它们在实现和性能方面有显著区别: 底层实现:map 是基于红黑树实现的有序关联容器,而 unordered_map 是基于哈希表实现的无序关联容器。元素…...
系统架构设计师【第9章】: 软件可靠性基础知识 (核心总结)
文章目录 9.1 软件可靠性基本概念9.1.1 软件可靠性定义9.1.2 软件可靠性的定量描述9.1.3 可靠性目标9.1.4 可靠性测试的意义9.1.5 广义的可靠性测试与狭义的可靠性测试 9.2 软件可靠性建模9.2.1 影响软件可靠性的因素9.2.2 软件可靠性的建模方法9.2.3 软件的可靠性模…...
x264 参考帧管理原理:i_poc_type 变量
x264 参考帧管理 x264 是一个开源的 H.264 视频编码软件,它提供了许多高级特性,包括对参考帧的高效管理。参考帧管理是视频编码中的一个重要部分,它涉及到如何存储、更新和使用已经编码的帧以提高编码效率。 x264 参考帧管理的一些关键点总结如下: 参考帧的初始化和重排序:…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案
在大数据时代,海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构,在处理大规模数据抓取任务时展现出强大的能力。然而,随着业务规模的不断扩大和数据抓取需求的日益复杂,传统…...
Unity中的transform.up
2025年6月8日,周日下午 在Unity中,transform.up是Transform组件的一个属性,表示游戏对象在世界空间中的“上”方向(Y轴正方向),且会随对象旋转动态变化。以下是关键点解析: 基本定义 transfor…...
数据结构:递归的种类(Types of Recursion)
目录 尾递归(Tail Recursion) 什么是 Loop(循环)? 复杂度分析 头递归(Head Recursion) 树形递归(Tree Recursion) 线性递归(Linear Recursion)…...
2025年低延迟业务DDoS防护全攻略:高可用架构与实战方案
一、延迟敏感行业面临的DDoS攻击新挑战 2025年,金融交易、实时竞技游戏、工业物联网等低延迟业务成为DDoS攻击的首要目标。攻击呈现三大特征: AI驱动的自适应攻击:攻击流量模拟真实用户行为,差异率低至0.5%,传统规则引…...
归并排序:分治思想的高效排序
目录 基本原理 流程图解 实现方法 递归实现 非递归实现 演示过程 时间复杂度 基本原理 归并排序(Merge Sort)是一种基于分治思想的排序算法,由约翰冯诺伊曼在1945年提出。其核心思想包括: 分割(Divide):将待排序数组递归地分成两个子…...
