linux入门---动静态库的加载
目录标题
- 为什么会有动态库和静态库
- 静态库的实现
- 动态库的实现
- 动静态库的加载
为什么会有动态库和静态库
我们来模拟一个场景,首先创建两个头文件
根据文件名便可以得知add.h头文件中存放的是加法函数的声明,sub.h头文件中存放的是减法函数的声明,既然有头文件那么也应该存在对应的源文件,所以这里再创建两个源文件:
然后就在头文件中添加函数的声明,在源文件中添加函数的实现即可,那么这里的代码如下:
//add.h
#pragma once
#include<stdio.h>
extern int add(int x ,int y); //add.c
#include"add.h"
int add(int x ,int y)
{ printf("enter Add func, %d + %d = ?\n", x, y); return x+y;
}//sub.h
#pragma once
#include<stdio.h>
extern int sub(int x ,int y); //sub.c
#include"sub.h"
int sub(int x ,int y)
{ printf("enter Sub func, %d - %d = ?\n", x, y); return x-y;
}
有了这四个文件之后我们就可以再创建一个文件,并且使用这两个源文件的内容来实现一些功能,那么这里我们就创建一个main.c文件:
main.c中的代码如下:
#include"add.h"
#include"sub.h"
int main()
{ int x =20; int y=10; printf("result: %d\n",add(x,y)); printf("result: %d\n",sub(x,y)); return 0;
}
然后我们就可以使用gcc指令来生成一个名为test的可执行程序,比如说下面的图片:
运行一下这个可执行程序便会出现下面这样的场景:
那么这就说明代码的实现是正确的。在之前的学习中我们知道可以用-c选项来生成二进制文件,并且我们还可以使用二进制文件来生成可执行程序,比如说下面的操作:
并且可执行程序的运行结果也是一摸一样的:
因为二进制文件的内容正常人是看不懂的,所以我们可以利用这个特性来传播一些保密的资源,比如说有个东西我想让你使用但是不想让你知道这个东西的底层实现逻辑时就可以使用二进制文件来实现:
是不是看起来很难受对吧,所以这就可以起到一个保密的功能
假设folder1是功能的发明者folder2是功能的使用者,那么folder2要想使用这个功能就得知道这个功能是如何实现的,所以得将add.o和sub.o文件复制到folder2文件夹里面,然后再将main.c文件赋值到folder2文件夹里面,比如说下面的图片:
然后我们在folder2文件夹里面想要再生成一个可执行程序时就会爆出这样的错误:
可以看到这里说没有找到add.h文件,说明给别人方法时不仅得给别人.o文件还得给别人.h文件:
再来到folder2文件夹里面生成可执行文件时就可以看到成功了:
并且运行一下可以看到结果跟之前的是一模一样的:
也就是说未来别人要用我们实现的函数的话我们就可以提供给别人.o文件(方法的实现)和.h文件(都用什么方法),但是这里存在一个问题如果存在很多个.o文件的话这里在传递和使用的时候都非常的麻烦,所以我们就尝试着将所有的.o文件都打一个包形成一个库,这样在传递一个功能和方法的时候给对方一个库文件即可,库文件就是多个.o文件形成的一个文件,而采用不同的工具和方法生成的库就称为静态库和动态库,库的本质就是.o文件的集合,那么接下来我们就来看看如何生成静态库和动态库,并且这两个库会存在什么样的区别。
静态库的实现
使用ar指令来创建静态库,ar的全程是archive,使用方法ar -rc 生成的库文件名 源文件名
,这样就可以生成一个静态库,比如说当前路径下的文件如下:
接下来我们要完成makefile文件里面的内容,静态库的命名规则是lib开头以.a结尾所以库的名称为libmymath.a,库文件依靠add.o文件和sub.o文件,实现的方法是ar -rc libmymath.a add.o sub.o
,那么makefile的第一条指令的内容如下:
libmymath.a:add.o sub.o ar -rc $@ $^
但是当前路径下没有add.o文件和sub.o文件,所以我们还得添加这两个文件的指令和对应的实现方法,那么这里的代码如下:
libmymath.a:add.o sub.o ar -rc $@ $^
add.o:add.c gcc -c add.c
sub.o:sub.c gcc -c sub.c
通过前面的例子我们知道要想传递一个功能不仅得传递.o文件还得传递对应的.h文件,所以这里为了传递方便我们就创建一个文件夹,文件夹中存在两个小文件夹一个名为lib用来存放所有的库文件,一个名为include用来存放所有的头文件,所以这里再添加一个output指令该指令的实现方法就是创建一系列的文件夹,将所有的.o文件放到一个文件夹里面将所有的.h文件放到另外一个文件夹里面,那么这里的代码如下:
libmymath.a:add.o sub.o ar -rc $@ $^
add.o:add.c gcc -c add.c
sub.o:sub.c gcc -c sub.c
.PHONY:output
output:mkdir -p mylib/includemkdir -p mylib/lib cp -f *.h mylib/includecp -f *.a mylib/lib
最后就是clean指令,这个指令就删除当前路径下的所有.o文件和库文件就可以了,那么makefile的完整代码如下:
libmymath.a:add.o sub.o ar -rc $@ $^
add.o:add.c gcc -c add.c
sub.o:sub.c gcc -c sub.c
.PHONY:output
output:mkdir -p mylib/includemkdir -p mylib/lib cp -f *.h mylib/includecp -f *.a mylib/lib
.PHONY:clean
clean:rm -f *.o libmymath.a
输入make指令变可以看到当前路径下出现了几个文件:
file一下libmymath.a文件便可以看到他说这个文件是一个归档文件,
然后为了方便讲库文件和头文件的传递,我们还要输入一个make output指令来生成文件夹集合:
然后我们就可以使用tar指令将生成的文件夹进行打包,然后就可以把打包文件放到yum源上,这样别人就可以使用yum来进行下载,或者将这个软件放到某个网站上供别人下载:
假设下载完成就是将这个打包文件放到上级目录的folder3目录里面:
那么下载完做的第一件事情就是将打包文件进行解压得到内部的文件:
然后将这个文件夹里面的头文件都安装到系统的头文件目录里面也就是/usr/include/
,将所有的库文件也拷贝到系统的库文件里面也就是/lib64/
,那么上面拷贝的过程就是安装,所谓的安装就是将目标文件拷贝到系统指定的路径下,通过这个路径系统可以找到这些文件,那么这时我们有了库文件和头文件按道理来说在folder3文件夹里面就可以使用这些函数来执行一些功能,所以我们就再把main.c文件复制到folder3文件里面然后再使用该文件生成一个可执行程序比如说下面的操作:
但是生成可执行程序的时候又会出现问题:编译器找不到头文件,我们知道编译器找查找头文件的时候默认在两个地方进行搜索一个当前路径下搜索,一个是在系统指定的路径下进行搜索,我们没有将当头文件和库文件下载到系统路径了里面,所以当前的搜索方式是在当前路径下进行查找,虽然头文件在folder3文件夹里面可是头文件太深了并没有和main.c文件位于同一水平上,所以找不到目标文件那么要想解决这里的问题就得告诉编译器在什么位置下搜索头文件,所以得添加-I选项和指定查找的路径,比如说下面的操作:
这里依然会报错但是这里报错的原因是链接错误,说明头文件找到了但是库文件没有找到,但是之前敲代码的时候我们并没有告诉编译器库文件在哪里?那为什么也能编译通过呢?因为c和c++的库也是在系统的默认路径下,也就是lib64和/usr/lib下就存放着库文件,所以还得添加-L选项并加上库文件的路径,比如说下面的操作:
但是运行之后还会报错,因为链接第三方库的时候还得告诉库的名称,因为系统不知道第三方库叫什么,但是之前写代码的时候从来没有指明过库的名称为什么还能正常运行呢?原因很简单并不是你不指明就也能正常通过而是编译器自动帮你填写了,为什么c++的编译器叫做g++c语言的编译器叫做gcc,因为这些编译器知道你写的程序缺少什么库,但是对于第三方的库他就无法得知了,所以添加-l加上库的名字,这里的名字得去掉前缀和后缀,比如说下面的操作:
生成可执行程序的时候我们还可以发现这里的连接是动态链接并且查看链接库的时候并没有接我们创建的库,因为gcc默认是动态链接,生成一个可执行程序的时候会链接多个库所以这里就会出现问题,gcc默认是动态链接但是这是一个建议过程,对于一个特定的库究竟是动态还是静态库还是取决于你提供的是动态库还是静态链接,如果动静态库都给你了这里的选着权就来到了gcc上面,如果有100个库70个是动态库还有30个是静态库,那么库在链接的时候还是一个一个链接,但是只要有一个库是动态链接的这个可执行程序就是动态链接的。这里在生成可执行程序的时候还是太麻烦了如果想要减少指令的话这里采用的方法就是将头文件都拷贝到/usr/include/里面,再将库文件拷贝到/lib64/里面,那么这种行为就是安装的过程将目标文件拷贝到指定路径下面,但是运行起来还是会报错因为不知道要使用哪个库,所以即便拷贝好了也得告诉程序你使用的是哪个库。
动态库的实现
动态库也是和之前一样先用gcc生成.o结尾的二进制文件,但是这里生成二进制文件的时候得添加一个-fPIC选项,这个选项就是在生成.o文件的时候产生与位置无关码,比如说下面的操作:
然后就对所有的.o文件进行归档,但是这里的归档和静态库的指令不一样,动态库是用gcc指令加 -shared选项进行归档,比如说下面的操作:
这样就生成了一个动态库,同样的道理这里再创建一个文件夹,文件夹里面还有两个小文件夹一个用来存放头文件一个用来存放库文件,然后将这个大文件拷贝到folder3文件夹里面,比如说下面的操作:
然后也是同样的道理,生成可执行程序的时候会告诉我们找不到头文件:
所以得添加-I选项来指明具体的路径:
然后还得告诉编译器库文件在哪里,所以还得添加-L选项:
光找到库的位置不行还得告诉编译器库的名字叫什么所以这里还得添加-l选项指名库的名称:
可以看到这里确实生成了可执行程序但是我们运行一下这个程序时便会发现这里依然是有问题的,并且使用ldd指令查看该文件连接库的情况时会发现libmymath.so动态库根本没有连接上去:
这里的错误表示库没有链接上去没有找到库,但是我已经告诉了库的名称库的位置和头文件了为什么还找不到了,原因很简单你这里的告诉是跟gcc说的,程序在编译链接的时候还和gcc有关吗?没关系了程序在运行的时候需要依靠操作系统和shell,所以操作系统和shell也需要知道库在哪里,操作系统和shell只会去系统路径下进行查找,而我们刚刚写的文件并不在那些路径下所以就找不到,所以就会出现上面这样的问题,那么要想解决这里的问题就得让操作系统和shell找到动态库,那么这里就存在多个方法来解决这里的问题:
方法一:修改环境变量
环境变量LD_LIBRARY_PATH中记录了操作系统默认查找的路径,
只要我们把库所在的路径导入到该环境变量里面操作系统就会在该路径下搜索库,比如说下面的操作:
这样库所在的路径就会导入到环境变量里面,并且再运行一下上面的程序便可以发现没有问题了:
那么这就是方法一修改环境变量,这种方法存在一个问题就是不持久,因为每次登录进入系统都会更新一次环境变量所以该方法是不持久的。
第二种方法:安装
这种方法就是将动态库拷贝到/lib64里面,这样操作系统就可以从系统库中找到我们写的库,但是我们写的库不一定是安全的,所以这里就不展示该方法的实现了,大家也不要尝试这样的方法。
第三种:配置文件法
在系统中存在这么一个路径:/etc/ld.so.conf.d/
,这个路径下存在很多的配置文件:
因为操作系统在查找库的时候会查询一下配置文件也就是.conf结尾的文件,所以我们可以通过创建配置文件的方式来让操作系统找到对应的动态库,那么这里我们就先创建一个.conf结尾的文件,然后将库所在的路径填入到新创建的文件里面:
然后就强行退出并保存文件,然后使用ldconfig指令更新一下所有的conf文件,这样我们就可以永久的正常的执行刚刚写的程序:
那么这就是方法三。
第四种方法:软链接
程序在搜索库文件的时候会默认在当前路径下进行搜索,所以我们可以在程序所在的路径下创建一个软连接让其指向动态库文件,比如说下面的操作:
然后再运行一下该程序便可以发现可以正常的运行:
动静态库的加载
静态库不考虑加载的过程,在生成可执行程序的时候静态库会把有关代码拷贝直接拷贝进程序里面(用了printf的代码就拷贝库中有关printf的代码),然后程序再加载进内存所以当有多个程序都采用静态链接的时候就会出现冗余的现象,这里存在一个问题将库的代码拷贝到我们的程序里面,那是拷贝到程序中的哪里地方呢?磁盘中的程序含有虚拟地质空间,库中的代码在磁盘上的代码区,所以生成可执行程序的时候是将库中的代码拷贝到程序的代码区,所以程序在查找函数的时候也是去代码区找查找。动态链接是将动态库中的指定函数的地址写入到可执行程序里面,因为printf函数在文件里面也有对应的地址,将该地址写入到可执行程序里面然后程序里面就可以根据该地址找到对应的方法,那这个地址是什么地址呢?我们之前说过一个东西叫做位置无关码那这个又是什么意思呢?我们根据一个生活例子来理解,假设一个马路上只有一个红绿灯,在红绿灯东边100米有一家餐馆,那么我们要想找到这个餐馆是不是只用找到这个马路上的红绿灯就可以了,只要找到了红绿灯就可以往东边走100米从而找到餐馆,那么我们把这样的地址称为相对地址,动态库加载进程序中的地址就是这样的相对地址,动态库的地址由两个地址组成一个start起始地址,一个是该函数在库中相对于库起始地址的偏移地址,start具体是多少在生成程序的时候我们还不知道,当操作系统执行程序发现该程序需要使用库函数的时候就会将该函数对应的动态库加载进内存里面,然后再通过页表将该库映射到虚拟内存中的共享区里面,最后将该库在地址空间上的起始地址填入到start里面,这样程序就可以根据start地址加上每个函数独有的偏移量来找到共享区上的库中的每个函数的内容,那么这就是动态库的加载,而静态库则是相对确定的,当库中的代码被加载进内存的时候库中的代码就已经确定了,可以直接根据地址进行查找,那么这就是动态库的加载。
相关文章:

linux入门---动静态库的加载
目录标题 为什么会有动态库和静态库静态库的实现动态库的实现动静态库的加载 为什么会有动态库和静态库 我们来模拟一个场景,首先创建两个头文件 根据文件名便可以得知add.h头文件中存放的是加法函数的声明,sub.h头文件中存放的是减法函数的声明&#…...

计算机竞赛 基于深度学习的人脸专注度检测计算系统 - opencv python cnn
文章目录 1 前言2 相关技术2.1CNN简介2.2 人脸识别算法2.3专注检测原理2.4 OpenCV 3 功能介绍3.1人脸录入功能3.2 人脸识别3.3 人脸专注度检测3.4 识别记录 4 最后 1 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 基于深度学习的人脸专注度…...

【Yolov5+Deepsort】训练自己的数据集(3)| 目标检测追踪 | 轨迹绘制 | 报错分析解决
📢前言:本篇是关于如何使用YoloV5Deepsort训练自己的数据集,从而实现目标检测与目标追踪,并绘制出物体的运动轨迹。本章讲解的为第三部分内容:数据集的制作、Deepsort模型的训练以及动物运动轨迹的绘制。本文中用到的数…...

docker desktop如何一键进入容器内部
对着对应的容器 点击 view files...

多机单目标跟踪Cross-Drone Transformer Network for Robust Single Object Tracking
1. 摘要 无人机已被广泛用于各种应用,如空中摄影和军事安全,因为与固定摄像机相比,无人机具有高机动性和广阔的视野。多架无人机跟踪系统可以通过收集不同视角的互补视频片段来提供丰富的目标信息,特别是当目标在某些视角下被遮挡…...

手写Mybatis:第7章-SQL执行器的定义和实现
文章目录 一、目标:SQL执行的定义和实现二、设计:SQL执行的定义和实现三、实现:SQL执行的定义和实现3.1 工程结构3.2 SQL执行实现的关系图3.3 执行器的定义和实现3.3.1 Executor 接口3.3.2 BaseExecutor 抽象基类3.3.3 SimpleExecutor 简单执…...

C语言基础知识理论版(很详细)
文章目录 前述一、数据1.1 数据类型1.2 数据第一种数据:常量第二种数据:变量第三种数据:表达式1、算术运算符及算术表达式2、赋值运算符及赋值表达式3、自增、自减运算符4、逗号运算符及其表达式(‘顺序求值’表达式)5…...

CG MAGIC分享3d Max中的Corona渲染器材质如何成转换VRay材质?
大家无论是使用Corona渲染器还是Vray渲染器时,进行材质问题时,都会遇到转化材质问题。 如何将CR转换成VR或者将VR转换CR材质呢? 对于这两者之间转换最好最好的方法只能是材质转换器。 CG MAGIC小编,梳理了两种方法,大…...

电脑入门:路由器常见问题排错步骤
HiPER系列路由器使用中Ping LAN口不通的诊断步骤 准备工作: 在可以ping通的时候记录下路由器LAN口的MAC地址: 命令hiper% show interface ethernet/1 mac Mac : 0022aa419d1e 以下步骤在ping不通路由器的时候依次操作,并记下结果: 步骤一:观察设备各端口…...

mac电脑识别不出来u盘?mac识别不了u盘怎么办
有些用户反馈说本来想要拷贝文件,但是将U盘插入mac系统后竟然不能识别,这时候我们需要用到NTFS For Mac软件。 其实mac系统只提供了它自身磁盘格式(mac os 扩展)等的读写权限,只提供了读的权限给NTFS、FAT32给硬盘和U盘,我们99%使…...

【系统编程】线程池以及API接口简介
(꒪ꇴ꒪ ),Hello我是祐言QAQ我的博客主页:C/C语言,数据结构,Linux基础,ARM开发板,网络编程等领域UP🌍快上🚘,一起学习,让我们成为一个强大的攻城狮࿰…...

Verilog零基础入门(边看边练与测试仿真)-笔记
文章目录 第一讲第二讲第三讲第四讲 第一讲 1、testbench 没有端口,所以没括号 2、testbench 输入端 之后要变动 所以定义为reg 3、#10 :过10个时间单位 ;’timescale 1ns/10ps 即 1ns 的时间单位 10ps的时间精度 4、reg 型变量赋值的时候 用…...

LLMs之Code:Code Llama的简介、安装、使用方法之详细攻略
LLMs之Code:Code Llama的简介、安装、使用方法之详细攻略 导读:2023年08月25日(北京时间),Meta发布了Code Llama,一个可以使用文本提示生成代码的大型语言模型(LLM)。Code Llama是最先进的公开可用的LLM代码任务,并有潜…...

[国产MCU]-W801开发实例-MQTT客户端通信
MQTT客户端通信 文章目录 MQTT客户端通信1、MQTT介绍2、W801的MQTT客户端相关API介绍3、代码实现本文将详细介绍如何在W801中使用MQTT协议通信。 1、MQTT介绍 MQTT 被称为消息队列遥测传输协议。它是一种轻量级消息传递协议,可通过简单的通信机制帮助资源受限的网络客户端。 …...

搭建个人hMailServer 邮件服务实现远程发送邮件
文章目录 1. 安装hMailServer2. 设置hMailServer3. 客户端安装添加账号4. 测试发送邮件5. 安装cpolar6. 创建公网地址7. 测试远程发送邮件8. 固定连接公网地址9. 测试固定远程地址发送邮件 hMailServer 是一个邮件服务器,通过它我们可以搭建自己的邮件服务,通过cpolar内网映射工…...

React的 虚拟DOM创建
React是一个流行的JavaScript库,用于构建用户界面。它通过使用虚拟DOM来提高性能和渲染速度。本文将详细介绍React的虚拟DOM的创建方式、用法和案例,以及相关代码和解释。 虚拟DOM是什么? 虚拟DOM是React的一个重要概念,它是一个…...

供热管网安全运行监测,提升供热管网安全性能
城市管网是城市的“生命线”之一,是城市赖以生存和发展的基础,在城市基础设施高质量发展中发挥着重要作用。供热管网作为城市生命线中连接供热管线与热用户的桥梁,担负着向企业和居民用户直接供热的重要职责。随着城市热力需求的急剧增加&…...

手写Mybatis:第14章-解析和使用ResultMap映射参数配置
文章目录 一、目标:ResultMap映射参数二、设计:ResultMap映射参数三、实现:ResultMap映射参数3.1 工程结构3.2 ResultMap映射参数类图3.3 添加类型处理器3.3.1 日期类型处理器3.3.2 类型处理器注册机 3.4 存放映射对象3.4.1 结果标志3.4.2 结…...

GE VME-7807RC-410001350-93007807-410001 K数字输入模块
通道数目: VME-7807RC-410001350-93007807-410001K 数字输入模块通常具有多个数字输入通道,可以同时监测多个数字信号。 输入类型: 这种模块通常用于监测数字信号,例如开关状态(ON/OFF)或计数器脉冲。 采…...

C++插入加密,替代加密
void 插入加密() {//缘由https://bbs.csdn.net/topics/396047473int n 1, j 0;char aa[60]{}, aaa[] "abcde";cin >> aa;while (j < 60 && (aa[j] - \0))cout << aa[j] << aaa[j % 5]; } void 插入加密() {//缘由https://bbs.csdn.n…...

Web前端开发概述
Web(World Wide Web,全球广域网)是指一种基于互联网的信息系统,通过超文本链接将全球各地的文档、图像、视频等资源相互关联起来,并通过Web浏览器进行交互浏览和访问。Web的发展使得人们可以方便地获取和共享各种类型的…...

Web自动化 —— Selenium元素定位与防踩坑
1. 基本元素定位一 from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By # selenium Service("../../chromedriver.exe") # driver webdriver.Chrome(serviceService) # driver.…...

【数据结构】树和二叉树的概念及结构(一)
目录 一,树的概念及结构 1,树的定义 2,树结点的分类及关系 3,树的表示 二,二叉树的概念及结构 1,二叉树的定义 2,特殊的二叉树 3,二叉树的性质 4,二叉树的存储结构 1&…...

第三章 USB应用笔记之USB鼠标(以STM32 hal库为例)
第三章 USB应用笔记之USB鼠标(以STM32 hal库为例) 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 第三章 USB应用笔记之USB鼠标(以STM32 hal库为例)前言一、STM32 U…...

微服务01-基本介绍+注册中心EureKa
基本介绍 服务集群:一个请求由多个服务完成,服务接口暴露,以便于相互调用; 注册中心:每个服务的状态,需要进行维护,我们可以在注册中心进行监控维护服务; 配置中心:这些…...

【ES6】JavaScript中的异步编程:async和await
在JavaScript中,异步编程是一种处理长时间运行的操作的方法,这些操作包括读取文件、网络请求或处理大数据等。在传统的回调函数中,代码按照顺序执行,一旦遇到长时间运行的操作,就需要回调函数来处理结果。这使得代码变…...

51单片机热水器温度控制系统仿真设计( proteus仿真+程序+原理图+报告+讲解视频)
51单片机热水器温度控制系统仿真设计 1.主要功能:2.仿真3. 程序代码4. 原理图5. 设计报告6. 设计资料内容清单 &&下载链接 51单片机热水器温度控制系统仿真设计( proteus仿真程序原理图报告讲解视频) 仿真图proteus7.8及以上 程序编译器&#x…...

Spring Boot 配置文件加密
方式一:Spring Cloud Config 一、建立config server 1. build.gradle 文件中添加: plugins {id javaid org.springframework.boot version 2.7.0id io.spring.dependency-management version 1.0.11.RELEASE }ext {set(springCloudVersion, "202…...

【树形权限】树形列表权限互斥选择、el-tree设置禁用等等
需求:按照权限管理配置的数据权限树展开;点击查看按钮后进入其他指定机构选择弹窗为一树形结构 本文章对项目中出现得关键点进行总结。 一、实现如上树形列表 在 element 官方表格示例中,实现树形表格列表数据渲染,非常简单。只…...

ubuntu 22.04安装cuda、cudnn、conda、pytorch
1、cuda 视频连接 https://www.bilibili.com/video/BV1bW4y197Mo/?spm_id_from333.999.0.0&vd_source3b42b36e44d271f58e90f86679d77db7cuda 11.8 https://developer.nvidia.com/cuda-toolkit-archive点击进入 https://developer.nvidia.com/cuda-11-8-0-download-arc…...