【Linux学习】动静态库从原理到制作

目录
- `🍑动静态库`
- `🐟动静态库的制作与使用`
- `🚀生成静态库`
- `🔒生成动态库`
- `🦌动态库的查找`
- `🐬动态库与静态库`
- `🐧动态库加载`
🍑动静态库
·
静态库 && 动态库
- 静态库(.a):程序在
编译链接
的时候把库的代码链接到可执行文件中。程序运行
的时候将不
再需要静态库。 - 动态库(.so):程序在
运行
的时候才去链接动态库的代码,多个程序共享使用库的代码。
一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码。 - 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为
动态链接
(dynamic linking)。 动态库的优点
:可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。
云服务器默认安装的是的动态库,静态库是没有安装的。
链接的时候默认的是动态链接
,如果想要静态链接,需要加-static 选项
,如:gcc test.c -static 库文件名称和引入库的名称
如:libc.so -> c库,去掉前缀lib,去掉后缀.so或则.a,剩余的就是库名称
。
🐟动静态库的制作与使用
·
为什么要有库?
- 提高开发效率
- 隐藏源代码
当不想暴露源文件
的前提下,想让外部使用我们的方法,最朴素的做法就是我们将自己的同名源代码编译成.o文件,然后给外部提供.h与.o文件,外部使用者只需要编写main.c文件,编译为main.o,然后够将所有的.o文件进行链接即可。这个过程就有点库的身影了。

🚀生成静态库
.c文件
编译形成.o
使用的指令:gcc -c 目标文件
(不指定形成的.o的文件名,默认形成同名的.o文件)
- 示例:默认已经有了.h与.c文件

- 将.o与.h文件给用户:

用户编写main函数并形成.o文件,将.o文进行链接形成可执行文件:

当.o文件很多的时候,在传文件给用户与正在进行链接的时候,不方便,可以将.o文件进行打包
,形成一个库:
打包所需要的指令:ar -rc 所形成的库的 .o文件
(该指令形成的是静态库
)
其中,ar是gnu归档工具,rc表示(replace and create)

链接的时候需要的指令及选项:gcc .c文件 -l库的名称 -L库所在路径
- -L 指定库路径
- -l 指定库名
目标文件生成后,静态库删掉,程序照样可以运行(很好理解,不赘诉)。

🔒生成动态库
- shared: 表示生成共享库格式
- fPIC:产生位置无关码(position independent code)
- 库名规则:libxxx.so

- 打包

当使用者拿到该库,编写main函数后直接编译时会发生如下图报错:链接时报错
。
原因
:因为使用gcc与g++编译的时候,默认可以识别C语言与C++的库与路径,而我们你所写的库是第三方库,所以不认识。

解决方法
:在编译的时候,告诉编译器库的位置
与名称
gcc编译的时候添加选项:gcc main.c -L. -lmyc
如果单纯在编译角度:不想设置 -L路径 选项 ,我们该怎么办。
我们只需要将我们所写的库拷贝到系统的默认搜索路径下
。
库搜索路径
- 从左到右搜索-L指定的目录。
- 由环境变量指定的目录 (LIBRARY_PATH)
- 由系统指定的目录 /usr/lib /usr/local/lib
查看可执行程序链接的动态库
指令:ldd 可执行程序

发布
自己所写的库:
将自己写的.h文件与形成的库放在一个目录中,就可以将该文件发布在网上或则给其他人了,如图:

其中头文件
的查找默认在当前路径与系统中头文件查找路径
下去查找,如果所写的头文件不在这两个路径下,需要-I
指明头文件位置:

如下所示:

注意
:虽然这时候有可以编译通过了,但是当我们运行的时候会报错
,这里与程序运行时动态库的查找有关,后面会详细说明以及给出解决办法。

但是上述的解决方法太麻烦,有没有方法不需要带那么长的选项呢?
方法:将头文件与库拷贝
到系统指定的目录
(一般都是root目录,需要超级用户权限)中去。
示例:
系统默认头文件搜索路径
:

库路径
:

- 将我们写的头文件与库拷贝到系统中指定路径:

再编译运行就不会报错了,如下图:

什么是库?
- 所谓的库,以上面的例子来说,就是将所有的.o文件用特定的方式,进行打包,形成一个文件。后面只需要提供一堆头文件+一个库文件。
🦌动态库的查找
对于上面遗留的报错:(进行解释与给出解决办法)
对于静态库
来讲,只要在编译后
形成了可执行程序,就不需要
使用静态库了(这个很多好理解,因为静态库就是编译时拷贝),但是对于动态库
而言,编译过后,在运行的时候,还会使用到动态库
,前面利用的gcc -I +后面的选项编译
,只是告诉编译器,在运行的时候,与编译器就没有关系了,所以在程序运行时OS加载程序就找不到库位置。
对于动态库
来讲:需要编译时的搜索路径与运行时的库搜索路径
。其中,两个路径可以一样,如果是自定义的,既需要告诉编译器,也需要告诉OS。
解决方法(四种)
:
方法一
:将库安装(拷贝)到系统中,这样既可以支持编译,也可以支持运行。
示例:

-
方法二
:将不系统默认库搜索路径下的库路径,添加到LD_LIBRARY_PATH环境变量
中。
该变量是系统运行程序的时候,动态库查找的辅助路径。 -
示例:注意:每次设置的时候都是内存级设置,重启系统就会消失。
-
方法三
:通过软链接
的方式
-
方法四
:配置/etc/ld.so.conf.d/
,ld config更新
在/etc/ld.so.conf.d/目录下了,用户可以新建文件名以.conf结尾
的配置文件,在配置文件中只需要写入你所要使用的第三方库的库路径
即可。
🐬动态库与静态库
- 如果我们同时提供静态库与动态库,gcc默认使用的是动态库;
- 如果非要静态链接,必须加-static选项;
- 如果只提供静态库,只能对该库进行静态链接,但是程序不一定整体是静态链接的;
- 如果只提供动态库,默认只能进行动态链接,非得静态链接,会发生链接报错;
🐧动态库加载
根据下图理解下面内容:
进程运行起来,程序的代码与数据被加载到内存,初始化进程的PCB的数据(初始化,未初始化,正文代码等等),CPU读取正文代码开始执行指令,当执行的指令需要用库里面的函数实现的时候,如果库没有被加载到内存就会先加载到内存中,然后通过页表建立映射,映射到进程地址空间的共享区,如果库已经被加载到内存中了,则直接映射,所以当我们需要访问库里面的方法时,只需要在地址空间中跳转到共享区,执行库中的方法,再返回到正文代码,就完成了库函数调用。
库函数的调用依旧是在进程地址中进行的,动态库加载后,会被映射到共享区中
。
当另外有程序也需要用到这个库的时候,只需要直接映射,不需要加载了,这样几个进程就可以共享一个被加载到内存中的库。所以,动态库也叫共享库
。
首先,可执行程序
本身是有自己的格式信息
的;
可执行程序在没有被加载到内存中,被编译号的可执行程序本来就有地址
(很好理解)!
可执行程序在加载之前基本上都按照类别(比如权限,访问属性等等)已经将可执行程序
划分为各个区域了,方便在运行时初始化地址空间的数据
(比如地址空间中各数据段的起始地址)。
其中,可以使用指令:size 可执行程序
查看划分的区域
我们之前所说的进程地址空间中有很多地址数据(比如初始化数据区的起始地址与结束地址)
,都是从可执行程序中来
的,下面会详细讲解到。
编址
分为绝对编址(平坦模式)
与相对地址(逻辑地址)
。
现在大多都是使用绝对地址编址
。
其中所说的地址,就是页表的虚拟地址
。
所以当在加载可执行程序
的时候,就可以根据可执行程序的相关内容去初始化进程地址空间与页表
所以,虚拟地址空间本身不仅OS要遵守,编译器在编译的时候也要遵守。
Linux系统
在编译的时候采用的是平坦模式编址
方式,在Linux可执行程序中的 虚拟地址=逻辑地址 。解释
:虽然逻辑地址对应的是相对地址(起始地址+偏移量),但是平坦模式也可以看成是起始地址+偏移量,只不过起始地址为0而已。
- 可执行程序在加载的时候会用可执行程序的
表头数据
来初始化进程的地址空间
,所以不同的可执行程序有不同的数据段的大小。 - 可执行程序加载到内存后,每一行代码都有自己的虚拟地址与物理地址的映射,图中只化了main函数第一条指令的映射示例。
在CPU中有一个cr3
寄存器,保存页表的起始地址,还有一个pc指针
(程序计数器),里面保存的是当在执行的指令的下一条指令的地址。程序被加载的时候,就会用可执行程序表头记录的入口地址(虚拟地址)将pc指针初始化,当准备工作都做完后,CPU就开始寻址,查页表,然后经过虚拟地址与物理地址转换(CPU内部完成),就可以依次有序的取指令,解析指令,执行指令。
所谓的地址空间,本质是由OS+编译器+CPU
三者共同配合完成的。
当加入动态库
动态库
在磁盘中是以库的起始地址+偏移量编址
的(如下图));
当可执行程序被加载到内存中时如果需要使用到库里面的函数时,都会将该位置改为库起始地址+该函数在库中的偏移量
;
所以当执行我们的程序时候,访问到图中的printf方法时,只需要将_start换成进程地址空间中库的起始地址,如图的0xFFF1111,然后再根据偏移量,在共享区确定该函数的虚拟地址,然后根据虚拟地址找到内存中该函数。
库数据和方法的访问,都是可以通过库地址在地址空间起始地址+我们程序内部的偏移量即可!
相关文章:

【Linux学习】动静态库从原理到制作
🍑个人主页:Jupiter. 🚀 所属专栏:Linux从入门到进阶 欢迎大家点赞收藏评论😊 目录 🍑动静态库🐟动静态库的制作与使用🚀生成静态库🔒生成动态库 🦌动态库的查…...

WPF篇(10)-Label标签+TextBlock文字块+TextBox文本框+RichTextBox富文本框
Label标签 Label控件继承于ContentControl控件,它是一个文本标签,如果您想修改它的标签内容,请设置Content属性。我们曾提过ContentControl的Content属性是object类型,意味着Label的Content也是可以设置为任意的引用类型的。 案…...

JavaFX对话框控件-ChoiceDialog
JavaFX对话框控件-ChoiceDialog 常用属性titlecontentTextinitOwnergraphicheaderTextdefaultValuechoicesdialogPane 常用事件显示事件setOnShowing显示事件setOnShown弹框按钮点击 综合案例自定义下拉框内容 与Alert大部分功能类似按钮不可以自定义多一个下拉框 常用属性 …...

一文了解BTC中的二层协议中Nervos network,CKB,RGB++,UTXO stack 之间的关系
注:该内容不构成投资建议,有些内容摘抄其他地方,如侵权,请联系删除。 Nervos network Nervos Network 是一个开源的区块链生态项目,该项目提供一套解决方案来应对区块链扩展性和互操作性的问题。 Nervos Network 成立…...

Oracle(47)如何创建和使用集合?
在PL/SQL中,集合(Collection)是一种复合数据类型,用于存储一组相关的数据项。集合主要有三种类型:关联数组(Associative Arrays)、嵌套表(Nested Tables)和可变数组&…...

SpringIOC和SpringAOC
lombok插件 XML<!-- 加载资源文件 --><context:property-placeholder location"classpath:jdbc.properties"></context:property-placeholder><!-- 注入数据源 --><bean id"dataSource" class"com.mchange.v2.c3p0.ComboP…...

static关键字详解
文章目录 static使用示例static底层原理静态初始化顺序静态与线程安全 static static是Java中的一个关键字,用于定义类级别的成员,类级别的成员是指那些属于整个类,而不是特定对象实例的成员。在Java中,类级别的成员包括静态变量…...

使用 Java RestClient 与 Elasticsearch 进行索引管理的示例
文章目录 准备工作测试连接创建索引查询索引是否存在删除索引总结 在这篇博客中,我将和大家分享如何使用 Java RestClient 与 Elasticsearch 进行简单的索引管理操作。如果你在开发过程中需要对海量数据进行高效搜索和分析,Elasticsearch 可能是个不错的…...

编程-设计模式 10:外观模式
设计模式 10:外观模式 定义与目的 定义:外观模式(Facade Pattern)提供了一个统一的接口,用来访问子系统中的一群接口。它定义了一个高层接口,让子系统更容易使用。目的:简化复杂的子系统的使用…...

非范型ArrayList和泛型List<T>
ArrayList 是 C# 中的一个非泛型集合类,它属于 System.Collections 命名空间。它提供了动态数组的功能,允许你在运行时添加、删除和访问元素。然而,需要注意的是,ArrayList 并不是类型安全的,因为你可以向其中添加任何…...

魔众文库系统v7.0.0版本推荐店铺功能,管理菜单逻辑优化
推荐店铺功能,管理菜单逻辑优化 [新功能] RandomImageProvider 逻辑升级重构,支持更丰富的随机图片生成 [新功能] 资源篮订单参数字段 [新功能] 首页推荐店铺功能,需要在后台 文库系统 → 文库店铺 开启推荐 [系统优化] Grid 快捷编辑请求…...

03、流程控制语句
01、位运算符 一、位运算符:是针对二进制数据(补码)的运算。(0看成false,1看成true) &:按位与——求出两个数字对应的二进制,有0则0 | :按位或——求出两个数字对应的二进制,有1则1 ^ :按位异或 ——求出两个数字对应的二进制,…...

[Android] [解决]Bottom Navigation Views Activity工程带来的fragment底部遮盖的问题
创建了Bottom Navigation Views Activity之后,在fragment_home.xml,加了一个RecyclerView, 后来添加了item之后发现底部会被盖住一部分。 解决:在layout里面加两句: android:paddingBottom"?attr/actionBarSize&…...

Armv8/Armv9架构中的原子性
B2.2 Arm 架构中的原子性 原子性是内存访问的一个特性,描述为原子访问。Arm 架构描述涉及两种类型的原子性:单次复制原子性和多次复制原子性。在 Arm 架构中,内存访问的原子性要求取决于内存类型,以及访问是显式的还是隐式的。有关更多信息,请参见: 单次复制原子性的要求…...

读零信任网络:在不可信网络中构建安全系统15协议和过滤
1. 协议 1.1. IKE/IPSec 1.1.1. 因特网密钥交换协议(Internet Key Exchange,IKE)用于执行IPSec认证和密钥交换 1.1.1.1. 通常以后台守护进程的方式实现,使用预共享密钥或X.509证书来认证对端并创建一个安全会话 1.1.2. IKEv1与IKEv2 1.1.2.1…...

C语言学习笔记 Day11(指针--中2)
Day11 内容梳理: 目录 Chapter 7 指针 7.6 指针 & 函数 (1)形参改变实参的值 (2)字符数组作为函数参数 1)合并字符串 2)删掉字符串中空格 (3)指针作为函数返…...

Golang 并发编程
Golang 并发编程 Goroutine 什么是协程 创建 Goroutine 主 goroutine (main函数)退出后,其它的工作 goroutine 也会自动退出 package mainimport ("fmt""time" )func myFunc() {i : 0for {ifmt.Println("func: …...

【数据结构详解】——选择排序(动图详解)
目录 🕒 1. 直接选择排序🕒 2. 堆排序 🕒 1. 直接选择排序 💡 算法思想:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始(末尾)位置…...

杂项命令(笔记)
ifconfig :http://t.csdnimg.cn/gT2AR echo :http://t.csdnimg.cn/6DSoO ps和top的区别 http://t.csdnimg.cn/f1XWt...

代码随想录算法训练营Day38||完全背包问题、leetcode 518. 零钱兑换 II 、 377. 组合总和 Ⅳ 、70. 爬楼梯 (进阶)
一、完全背包问题 相较于01背包,完全背包的显著特征是每个物品可以用无数次,遍历顺序也不需要为了保证每个物品只去一次而倒序遍历。 #include<iostream> #include<vector> using namespace std; int main(){int N,V;cin>>N>>V…...

超越链端:Web3的无边界技术革命
Web3,作为互联网技术的第三代变革,正以其去中心化、开放透明的特性,重新定义着我们的数字生活。在这一背景下,“链端”概念逐渐成为热点,意味着我们不仅仅局限于区块链技术本身,而是探索其在更广泛领域的应…...

127. Go反射基本原理
文章目录 反射基础 - go 的 interface 是怎么存储的?iface 和 eface 的结构体定义(runtime/iface.go):_type 是什么?itab 是什么? 反射对象 - reflect.Type 和 reflect.Value反射三大定律Elem 方法reflect.…...

提高PDF电子书的分辨率
解决方法出处 1. 安装ImageMagick brew install imagemagick brew install ghostscript2. 按流程进行 convert -density 600 your_pdf_filename.pdf output-%02d.jpg convert output*.jpg -normalize -threshold 80% final-%02d.jpg convert final*.jpg my_new_highcontras…...

Spring Cloud全解析:注册中心之zookeeper注册中心
zookeeper注册中心 使用zookeeper作为注册中心就不需要像eureka一样,在写一个eureka-server的服务了,因为zookeeper本身就是一个服务端,只需要编写需要进行服务注册的客户端即可 依赖 <!-- zookeeper 注册中心 --> <dependency&g…...

解决戴尔台式电脑休眠后无法唤醒问题
近期发现有少量戴尔的台式机会有休眠后无法唤醒的问题,具体现象就是电脑在休眠后,电源指示灯以呼吸的频率闪烁,无论怎么点鼠标和键盘都没有反应,并且按开机按钮也没法唤醒,只能是长按开机键强制关机再重启才行…...

MySQL运维-分库分表
介绍 问题分析 拆分策略 垂直拆分 水平拆分 实现技术 Mycat概述 介绍 概念介绍 Mycat配置 schema.xml schema标签 schema标签(table) datanode标签 datahost标签 rule.xml sever.xml system标签 user标签 Mycat分片 分片规则-范围 分片规则-取模 分…...

AGX orin硬件设计
AGX orin简介 从硬件组成来说,AGX orin可以分为核心板和扩展板。 核心板 核心板就是英伟达原装板卡,如下图所示: 核心板分为32G内存版本和64内存版本,两个版本除去内存不同之外,CPU也略有差异。核心板通过…...

AI大模型开发——2.深度学习基础(1)
学习大模型开发之前,我们需要有足够的储备知识,类似于基础的python语法相信大家也都是十分熟悉了。所以笔者也是考虑了几天决定先给大家补充一些深度学习知识。 首先问大家一个问题,学习大模型之前为什么要先学习深度学习知识呢? …...

go语言day22 gin-vue-admin全栈项目的依赖安装
flipped-aurora/gin-vue-admin: 🚀ViteVue3Gin的开发基础平台,支持TS和JS混用。它集成了JWT鉴权、权限管理、动态路由、显隐可控组件、分页封装、多点登录拦截、资源权限、上传下载、代码生成器【可AI辅助】、表单生成器和可配置的导入导出等开发必备功能…...

PHP之docker学习笔记
Docker学习笔记 前言: 之前学过一遍忘了 那就再来一遍没啥好说的就是可以直接构建一个环境 然后方便部署官网 http://www.docker.com仓库 https://hub.docker.comDocker的基本组成 镜像 容器 仓库 安装与卸载 卸载 sudo yum remove docker \docker-client \dock…...