Linux:动静态库的概念与制作使用

文章目录

  • 动静态库基础认知
  • 动静态库基本概念
  • 静态库的制作
    • 库的概念
    • 包的概念
  • 静态库的使用
    • 第三方库
    • 小结
  • 动态库的制作
  • 动态库的使用
    • 动态库如何找到内容?
    • 小结

本篇要谈论的内容是关于动静态库的问题,具体的逻辑框架是建立在库的制作,库的使用,和库的原理来展开,基于上述的三个模块来对动静态库有一个较为清楚的认知

动静态库基础认知

在前面的学习中知道,在用户写完代码后,想要将写完的代码转换成可以执行的可执行程序过程是一个相当复杂的过程,那么在这段过程中要处理的过程基本有,例如预处理,编译,汇编,链接,可执行程序,而在学习编译工具gcc的时候又提到过,在代码进行链接的过程中,系统必须要提供对应的动静态库,因此就引入了动静态库的概念

所谓动态链接,就是让程序和库产生这种地址性的关联,而静态库,就是把目标的库文件直接拷贝到对应的可执行程序当中

动静态库基本概念

对于静态库,第一步要先引入的是静态库的原理,静态库就是在进行编译链接的过程中,把静态库中所包含的代码都拷贝到可执行程序中,之后在可执行程序的运转就不需要静态库了

对于动态库,原理是在程序运行的时候才会链接动态库的代码,多个程序会共享使用库的代码,而一个与动态库链接的可执行文件只有一个函数入口地址的表,而不是整个文件的内容,上述是关于动态库的基本原理,关于这些内容后面就进行一个一个的解析

静态库的制作

首先,创建出对应的文件,这里假设要实现一个计算器,那么实现对应的函数声明和实现过程:

在这里插入图片描述
再创建一个对应的测试函数

#include "Add.h"
#include "Sub.h"
#include "Mul.h"
#include "Div.h"int main()
{int a = 10;int b = 20;printf("%d + %d = %d\n", a, b, Add(a, b));printf("%d - %d = %d\n", a, b, Sub(a, b));printf("%d * %d = %d\n", a, b, Mul(a, b));return 0;
}

那么现在准备工作就完成了,下面的问题是,我想要编译生成一个可执行程序,应该输入什么指令呢?

gcc -o test.exe test.c Add.c Sub.c Mul.c Div.c

这样就完成了编译,生成了一个可以被运行的可执行文件,那么下面对于上面的现象提出一些问题

1. 为什么在编译的时候不带头文件?

因为头文件所属的位置就在当前路径下,编译器是直接可以找到的,如果头文件对应的查找路径是在当前目录或者是指定的目录,是不需要写在编译选项中的

2. 上面的步骤是否每一步都需要?

这个问题问法很奇怪,怎么说是每一步都需要,假设现在要生成的是一个可执行程序,其实根本不太需要把源文件全部编译生成一个可执行文件,因为这样的过程需要经过预处理编译汇编链接等等,而实际上在编译这样的多文件项目的过程中,只需要把源文件都编译为.o后缀的文件,再将这些文件进行链接形成一个可执行文件,这是被倡议的一种链接方式

库的概念

这也就是在任何项目中,都会存在这样的文件的原因,在进行编译生成可执行文件的过程中,如果是直接将已经生成的这些.o后缀的文件进行一定的链接组合,就能生成可执行程序

基于上面的原因,我们重新进行一次编译,这次按照上述的过程生成对应的.o文件即可,为了方便后续进行其他的使用,写一个Makefile来自动进行编译比较好:

%.o:%.cgcc -c $<Test:Add.o Sub.o Mul.o Div.o test.ogcc -o $@ $^.PHONY:clean
clean:rm -rf *.o Test

那么上面就是实现了Makefile,但是和前面写的不太一样,这个Makefile是直接将这些.o后缀的文件生成了一个可执行程序,所以现在就要先生成这样的.o后缀的文件,借助gcc编译工具就可以生成,之后就可以运行出结果了

这里补充一点上面写的这个语句,%.c的意义就是类似于一种通配符,因为后续生成test可执行文件是依赖于.o文件的,但是它们都不存在,那么此时就需要根据依赖关系来进行推导,而在推导的过程中,当Makefile在进行被编译的时候,就会把%.c全部展开,之后就会进行不断的推导,展开成四个gcc的编译方式语句,而这里的$<表示的是把文件依赖列表中的内容一个一个的传递到下面的命令中,最后连起来,就能解析成对应的内容了

那么现在的问题是,如果源文件已经不需要了,而是只需要这些.o为后缀的文件,也就是说,把这些文件进行打包,作为一个库,而把这个包交给使用者后,使用者就只需要写出自己的.c文件,再编译成.o文件,就能和我刚才打包好的包直接进行链接,就省去了前面的很多步骤,像这样的过程就是前面所述的核心观点,基于这样的原因,现在当前的主要任务就是生成一个库

那么就对Makefile进行改造,改造的核心思路就是基于上述的这一系列原理,将文件编译成.o文件,再将文件整合到一个固定的地方,这就是库的概念

包的概念

上面的思路原理存在,下面的一个问题是,直接把.o文件存到一个固定的位置,显然是不太合适的方式,如果此时有几百个文件呢?如果也是一个一个的进行转移,那么可能会有所遗漏,这都是不被建议和允许的,基于这样的原因,有了打包的概念

ar指令
ar -rc $@ $^

上述就是在Linux中打包的指令,ar命令就是把所有的源文件进行打包形成对应库文件的过程,其中这个rc表示的是replace和create的意思,表示的是如果不存在就创建,存在就替换,总之这样就可以形成一个完整的.a文件

下面要做的就是生成一个库文件,库文件的生成也能放到Makefile中来写,具体的书写过程如下

# 库的名字是mymath,是个静态库
static-lib=libmymath.a# 生成库需要Add.o Sub.o Mul.o Div.o,实现方式是ar指令
$(static-lib):Add.o Sub.o Mul.o Div.oar -rc $@ $^# 生成.o文件需要把.c文件按照下面的gcc编译选项一个一个生成($<)
%.o:%.cgcc -c $<# 建库
.PHONY:output
output:mkdir -p mymath_lib/includemkdir -p mymath_lib/libcp -f *.h mymath_lib/includecp -f *.a mymath_lib/lib# 清空内容
.PHONY:clean
clean:rm -rf *.o *.a mymath_lib

上述的完整Makefile进行推导解析:第一行表示这是一个静态库,静态库的名字叫做mymath,前面的lib和后面的.a都是前缀和后缀,静态库真正的名字叫做mymath,而后面对于这个静态库的生成方式有了一个定义,静态库的生成依赖的是后面的四个.o文件,而具体的生成方式是ar指令,那么此时Makefile就会进行推导,Makefile现在需要.o文件,但是现在没有,所以在后面就提到了生成.o文件的过程,是利用.c文件来生成的,这样就完成了Makefile的过程,后面的两个操作就是建库和清除的过程

执行结果如下:
在这里插入图片描述
这样就完成了一个静态库,那么接下来要进入的问题是,库的使用问题

静态库的使用

进入这个话题,就意味着现在我们已经有了静态库,但是这个库怎么用呢?

朴素做法

现在已经有了库,别人给我提供了这些方法,我该如何使用?所以就用到了这些头文件提供的函数,但是现在如果直接编译会发现根本编译不过去,说明就现在而言,还是不可行的

在这里插入图片描述
原因在于什么呢?从报错信息来看,找不到这里对应的头文件,原因在于这些库文件都是被保护起来的,现在在本地编写之后的代码是无法找到对应的内容,说明现在还得想办法把这些库文件都让编译工具能够找见才可以,那么就把头文件都放到代码所在的目录中,再进行编译:

在这里插入图片描述
此时报错信息是,没有定义,说明现在已经找到对应的头文件了,但是没有找到定义头文件的地方,这个就叫做链接报错,那我写的这个库为什么用不了呢?

第三方库

对于我们自己写的库函数,都叫做第三方库,而gcc不认识第三方库,哪怕是就在当前路径下,也依旧不认识这个第三方库,因此就引出了要链接库的概念,所以就要引出一个选项,大I

-I 选项表示的意义是link,也就是链接指定的一个库,也就是说告诉编译器,你在进行编译的时候要使用这个库,所以执行下面的指令

gcc test.c -I mymath_lib/include/ -l mymath -L mymath_lib/lib

上面这一串是很长的指令,但是不急,一点一点的分析

首先是,要进行编译的对象是test.c,后面的这个选项表示的是新增头文件的搜索路径,后面紧跟着的就是头文件的搜索路径,而后面的小l表示的是指明链接的库名称,而大L表示的是新增库文件的搜索路径,基于上面这么一长串的选项,就能最终编译出来我们想要的结果,事实上也确实生成了,这说明我们的静态库已经使用成功了

之前我们使用的C标准库从来不需要指定,而此时为什么这里就需要指定了呢?因为这里我们自己实现的叫做第三方库,而gcc是专门用来处理C语言的编译工具,所以在进行编译的时候会直接到指定的路径下去寻找,gcc已经认识了C语言提供的官方库,而我们自己实现的第三方库它并不认识,即使看见了也不认识,需要我们主动的为gcc和自己写的库建立起合适的联系,才能让他们之间认识,编译器才能进行工作编译链接等等的后续操作,最终生成一个可执行程序

因此得出的结论是,未来我们把我们写的静态库提供给别人去使用,只需要把对应的.h头文件和对应的.a文件交给别人就够了,其中这个.a文件就是我们前面所说的.o文件的集合

那么下一个问题是,当使用ldd指令去查看依赖关系的时候,却发现一个问题

在这里插入图片描述
问题是,我们生成的这个a.out并不依赖我们写的库文件,这是因为在默认的情况下,可执行程序都是动态链接,因此ldd指令只能查询动态库,而静态库在编译期间就已经被拷贝到可执行程序当中了,因此也就查不到对应的信息,静态库是无法检查的

这里引出一个结论:gcc默认采取的是动态链接,但是对于个别库来说,如果你只提供.a的方式,那编译器也无能为力,只会把内容局部性的作为静态链接,而其他库则采取的是正常的的动态链接,如果带有 -static选项,那就必须要采取静态链接的方式了

所以说,在使用gcc进行编译的时候,如果这个程序依赖10个库,那么gcc就会尽量的把这10个库对应的.so文件都拿到,但是如果没有动态库也没关系,还可以去拿静态库

小结

加入现在需要某个库,我们从网上去下载,得到了库,下一步应该安装库,那如何安装库?实际上就是把对应的头文件和库文件都安装到系统当中,怎么安装到系统?本质上就是把对应的文件安装到usr路径下的include路径和lib路径下,所以说,安装的本质,就是把头文件和库文件分别拷贝到系统的指定路径下,只要拷贝到gcc的默认路径下,那么gcc在进行搜索的过程就不是问题

动态库的制作

关于动态库如何制作呢?其实也和静态库类似,从原理上将和静态库都相同,都是在源文件编译成.o文件后,给这些个文件进行打包,就形成了动态库,区别是,在形成对应的.o文件时,需要带上一个fPIC的选项,这个选项的意思是与位置无关码,至于这个是什么意思在后续会有讲解,这里只需要知道是这样的原理即可,具体原因主要是因为,动态库本身没有把内容拷贝到可执行程序当中去,因此动态库和可执行程序之间只是地址方面的关联,因此使用了动态链接后,只是告诉了可执行程序,你所需要的内容在哪里,在哪一个文件的什么位置,你需要的时候自己去找就可以,那么这个过程就叫做动态链接,所以在使用的时候只需要带上一个fPIC就可以了,之后再对生成的.o文件进行打包,使用的命令还是gcc命令,生成一个.so的文件,但是要带上-shared选项,表示的这个文件我想要生成的是一个共享库,也叫做是动态库

不管是在Linux中还是Windows中也好,动态库是比较重要的,形成动态库不需要额外的工具,只需要gcc就可以帮助我们完成这个过程,从这个角度也能看出,形成动态库的方法直接内置到了编辑中,但是静态库没有做出对应的内置,这也就说明动态库的重要性,那么下面对于Makefile进行对应的改造,生成我们所需要的动态库:

# 库的名字是mymath,并且是个动态库
dy-lib=libmymath.so# 动态库的生成方式是用gcc,带上编译选项,直接编译就可以
$(dy-lib):Add.o Div.o Mul.o Sub.ogcc -shared -o $@ $^# 生成动态库所需要的.o文件需要依赖于.c文件生成,并且也需要带上特殊选项,表示的是与位置无关码
%.o:%.cgcc -fPIC -c $<# 整体将生成的内容进行打包
.PHONY:output
output:mkdir -p mymath_lib_so/includemkdir -p mymath_lib_so/libcp -f *.h mymath_lib_so/includecp -f *.so mymath_lib_so/lib# 对部分内容做出清理
.PHONY:clean
clean:rm -rf *.o *.so mymath_lib_so

动态库的使用

如果想对于这个动态库把它安装到系统中,那么就需要放到指定的路径下,那么现在我们先不对于它做出任何操作,只是和静态库一样来尝试编译它,结果是:

在这里插入图片描述
事实上,用静态库的编译方式来对动态库进行编译,也成功了,说明到现在为止和静态库比起来没有任何区别,那么接下来接续:

在这里插入图片描述
在运行程序的过程中失败了,不过这也是可以预见的,因为静态库相当于直接把内容拷贝进去了,而动态库只是告诉你该去哪找,而在运行的时候,程序并不知道去哪找,所以找不见,这样的结果也是意料之内的

动态库和你的可执行程序是分离的,是两个文件,当执行程序的时候,程序需要被加载到内存,而库中的文件也要能够被系统找到,也是要加载到内存中,所以说,动态链接的程序,程序和库文件是分开的,在进程进行加载的过程中,程序库也要被找到并且加载,只有程序库被加载了,才能跑的起来,动态链接非常依赖动态库

那么如何能找到对应的内容呢?下面讨论的就是这个问题

动态库如何找到内容?

1. 直接安装到系统中

这个是简单粗暴的方法,当然也是简单可行的,看下面的操作

把头文件放到系统中
在这里插入图片描述
把库文件放到系统中
在这里插入图片描述

此时运行程序,就可以运行起来了,因为进程在运行的过程中可以在默认路径下找到我们所需要的内容,就能把这些内容加载到内存中供进程使用,调度等等
在这里插入图片描述

如果把对应的文件从对应的库中删除,那么就不能再使用了:

在这里插入图片描述
2. 软链接的方式

在这里插入图片描述
建立链接后,再进行编译运行,也能正常运行出结果

在这里插入图片描述
说明这也是可行的,同时查看ldd情况,发现确实是存在链接情况

在这里插入图片描述
现在解除链接,链接断开也就无法运行了

在这里插入图片描述
3. 通过环境变量的方式

这个方式也很好理解,默认的寻找方式是到lib64下去寻找,然而在这里可以通过修改环境变量,使得环境变量中新增一个配置变量,这样就能继续寻找了,具体操作过程如下:

在这里插入图片描述
但是环境变量的修改是临时的,这样的修改只在当次生效,当下次重新登陆就不存在了,这是因为环境变量在每次登陆时,都会由配置文件对其进行修饰,所以最根本的方法,其实是修改配置文件,这样就能每次都修改成功环境变量

4. 修改配置文件

在Linux中,动态库的配置文件的位置在 /etc/ld.so.conf.d/

那么修改的原理就是在这里新增一个文件,在文件中写入我们需要的地址就可以了!

具体操作展示如下:

在这里插入图片描述
所以往后你自己需要使用动态库,不管是用别人的还是你自己写的库文件,如果运行是找不到,那么这里就有四种做法

小结

如果同时同一组方法动静态库同时被提供,那么默认使用的是动态库,并且同一组库会提供动静态两种方式,gcc默认使用的是动态库,如果想使用静态库,就带上-static选项

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

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

相关文章

mysql INSERT数据覆盖现有元素(若存在)

INSERT...ON DUPLICATE KEY UPDATE的使用 如果指定了ON DUPLICATE KEY UPDATE&#xff0c;并且插入行后会导致在一个UNIQUE索引或PRIMARY KEY中出现重复值&#xff0c;则会更新ON DUPLICATE KEY UPDATE关键字后面的字段值。 例如&#xff0c;如果列a被定义为UNIQUE&#xff0…

不要为了学习而学习

经常有朋友问我&#xff1a; 老师&#xff0c;从您这里学了很多方法&#xff0c;也一直想要改变自己&#xff0c;但总是没办法坚持下去&#xff0c;怎么办&#xff1f; 这个问题&#xff0c;我也很无奈啊。毕竟我也没办法飞到你身边&#xff0c;手把手把每一步都教给你。&…

Eyes Wide Shut? Exploring the Visual Shortcomings of Multimodal LLMs

大开眼界&#xff1f;探索多模态模型种视觉编码器的缺陷。 论文中指出&#xff0c;上面这些VQA问题&#xff0c;人类可以瞬间给出正确的答案&#xff0c;但是多模态给出的结果却是错误的。是哪个环节出了问题呢&#xff1f;视觉编码器的问题&#xff1f;大语言模型出现了幻觉&…

七八分钟快速用k8s部署springboot前后端分离项目

前置依赖 k8s集群&#xff0c;如果没有安装&#xff0c;请先安装 kubectl &#xff0c;客户端部署需要依赖 应用镜像构建 应用镜像构建不用自己去执行&#xff0c;相关镜像已经推送到docker hub 仓库&#xff0c;如果要了解过程和细节&#xff0c;可以看一下&#xff0c;否…

基于springboot+vue的足球青训俱乐部管理系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 研究背景…

C++逆向分析--虚函数(多态的前置)

先理解一件事&#xff0c;在intel汇编层面来说&#xff0c;直接调用和间接调用的区别。 直接调用语法&#xff1a; call 地址 硬编码为 &#xff1a;e8 间接调用语法: call [ ...] 硬编码为: FF 那么在C语法中&#xff0c;实现多态的前提是父类需要实现多态的成员…

「 网络安全术语解读 」通用攻击模式检举和分类CAPEC详解

引言&#xff1a;在网络安全领域&#xff0c;了解攻击者的行为和策略对于有效防御攻击至关重要。然而&#xff0c;攻击模式的描述和分类方式缺乏统一性和标准化。为了解决这个问题&#xff0c;MITRE公司创建了CAPEC标准&#xff0c;以提供一个共享和统一的攻击模式分类框架。 1…

一文让你彻底搞懂cookie和session产生漏洞的原理

首先让我们来看看登录的一般流程&#xff1a; 输入账号密码提交给后端&#xff1b;后端进行判断账号密码是否一致&#xff0c;这里的逻辑根据每个程序员的想法去写&#xff1b;如果通过2登录成功&#xff0c;跳转登录成功的页面&#xff1b; 如果通过2登录失败&#xff0c;跳转…

Tensorflow2.0笔记 - 范式norm,reduce_min/max/mean,argmax/min, equal,unique

练习norm,reduce_min/max,argmax/min, equal,unique等相关操作。 范数主要有三种&#xff1a; import tensorflow as tf import numpy as nptf.__version__#范数参考&#xff1a;https://blog.csdn.net/HiWangWenBing/article/details/119707541 tensor tf.convert_to_tensor(…

TensorRT英伟达官方示例解析(二)

系列文章目录 TensorRT英伟达官方示例解析&#xff08;一&#xff09; TensorRT英伟达官方示例解析&#xff08;二&#xff09; 文章目录 系列文章目录前言一、03-BuildEngineByTensorRTAPI1.1 建立 Logger&#xff08;日志记录器&#xff09;1.2 Builder 引擎构建器1.3 Netwo…

【GitHub项目推荐--Awesome-Go/Python/JavaScript/Java】【转载】

Awesome 译为令人惊叹的、极好的&#xff0c;GitHub 上有很多 Awesome 开头的开源项目。比如 Awesome-Go、Awesome-Python。 就像汇总常用的软件一样&#xff0c;GitHub上有大量的开源项目&#xff0c;开发者就会根据需要汇总一些常用的好用的资源&#xff0c;并且根据 Awesom…

OCP NVME SSD规范解读-7.TCG安全日志要求

在OCP NVMe SSD规格中&#xff0c;TCG的相关内容涉及以下几个方面&#xff1a; 活动事件记录&#xff1a; NVMe SSD需要支持记录TCG相关的持久事件日志&#xff0c;用于追踪固态硬盘上发生的与TCG安全功能相关的关键操作或状态变化&#xff0c;如启动过程中的安全初始化、密钥…

当键入网址后,到网页显示,其间发生了什么

解析 URL&#xff1a; 浏览器地址栏输入 URL&#xff0c;浏览器解析 URL&#xff0c;从而生成发送给 web 服务器的请求信息&#xff08;例如 www.example.com&#xff09;。 检查域名缓存&#xff1a; 浏览器查看浏览器缓存系统缓存路由缓存&#xff0c; 如有存在缓存&#x…

SQL注入实战:盲注

盲注&#xff1a; 1、当攻击者利用SQL注入漏洞进行攻击时&#xff0c;有时候web应用程序会显示&#xff0c;后端数据库执行SQL查询返回的错误信息&#xff0c;这些信息能帮助进行SQL注入&#xff0c;但更多时候&#xff0c;数据库没有输出数据web页面&#xff0c;这是攻击者会…

JRT集中打印

之前一直在夯实基础&#xff0c;现在是补demo的时段了。了解过检验集中打印的人知道&#xff0c;集中打印的逻辑有多复杂。既要考虑普通检验报告加上换页。又要考虑微生物报告加上换页&#xff0c;既有A5的报告&#xff0c;也有A4的报告&#xff0c;还要考虑A4打印两个组装A5时…

工程化代码管理高频面试题

1. git常用命令以及工作中都怎么工作 git init 初始化仓库 ​ git status 查看当前各个区域的代码状态。 ​ git log查看commit记录 ​ git reflog查看完整记录 ​ git add 添加工作区代码到暂存区 ​ Git commit 暂存区代码的提交 ​ git reset 代码的版本回退 ​ git stash …

《WebKit 技术内幕》学习之七(3): 渲染基础

3 渲染方式 3.1 绘图上下文&#xff08;GraphicsContext&#xff09; 上面介绍了WebKit的内部表示结构&#xff0c;RenderObject对象知道如何绘制自己&#xff0c;但是&#xff0c;问题是RenderObject对象用什么来绘制内容呢&#xff1f;在WebKit中&#xff0c;绘图操作被定…

finalshell连接linux的kali系统

kali的ssh服务似乎是默认关闭的&#xff0c;笔者在玩CentOS系统时可以直接用finalshell完成连接&#xff0c;但kali不行&#xff0c;需要先手动开启ssh服务。 开启kali的ssh服务 输入【ssh start】命令开启ssh服务&#xff0c;可以用【ssh status】命令查看ssh状态&#xff0c…

第40集《佛法修学概要》

请大家打开讲义第一百零六页。我们讲到大乘的果位。大乘佛法的修学跟小乘最大的差别&#xff0c;主要在于一句话&#xff0c;就是大乘佛法是一种“称性起修&#xff0c;全修在性”。大乘佛法的功德第一个“称性”&#xff0c;这个“称”就是随顺。我们一念明了的心&#xff0c;…

【数据结构】 链栈的基本操作 (C语言版)

目录 一、链栈 1、链栈的定义&#xff1a; 2、链栈的优缺点&#xff1a; 二、链栈的基本操作算法&#xff08;C语言&#xff09; 1、宏定义 2、创建结构体 3、链栈的初始化 4、链栈的进栈 5、链栈的出栈 6、获取栈顶元素 7、栈的遍历输出 8、链栈的判空 9、求链…