你不知道的库:库的种类,作用和加载方式

你不知道的库:库的种类,作用和加载方式

📟作者主页:慢热的陕西人

🌴专栏链接:Linux

📣欢迎各位大佬👍点赞🔥关注🚓收藏,🍉留言

本博客主要内容讲解了库的概念和为什么要有库,以及静态库和动态库,最后还有最重要的库的加载的理解以及动态库的三种配置方法

文章目录

  • 你不知道的库:库的种类,作用和加载方式
    • 1.了解一下库
    • 2.为什么要有库
    • 3.写一写----从库的书写者的角度
      • 3.1库的命名规则:
    • 4.用一用----站在使用者的角度去使用我们写的库
      • 4.1静态库
      • 4.2动态库
    • 5.动态库配置问题
      • 5.1环境变量
      • 5.2软链接方案
      • 5.3配置文件方案
    • 6.动态库加载过程的理解
      • 6.1 动态库加载问题和周边问题:
      • 6.2库中地址的理解

1.了解一下库

虽然我们目前没有使用过第三方库,但是我们一直在使用一些C/C++内部的标准库,那么我们来在目录中查看一下这些库:

包括我们甚至可以找到我们经常使用的stdio.h标准库,并且可以使用vim编辑器进行查看的!

image-20231121142639688

所以我们可以理解到:

①系统已经预装了C/C++的头文件和库文件,头文件提供方法说明,库提供方法实现,头和库是有对应关系的,是要组合在一起使用的。

②头文件是在预处理阶段就引入的,链接的本质其实就是链接库!

另外我们也可以理解一些现象了:

① 所以我们在vs2019,2022下安装开发环境—安装编译器软件,其实就是安装要开发的语言配套的库和头文件

②我们在使用编译器的时候,都会有语法的自动提醒功能,需要先包含头文件。**语法提醒本质:**编译器或者编辑器,它会自动的将用户输入的内容,不断的在被包含的头文件中进行搜索,自动提醒功能依赖于头文件的。

③我们在写代码的时候,我们的环境怎么知道我们的代码中有那些地有语法报错,那些地方定义变量有问题?不要太小看编译器,编译器有命令行模式,也就是我们在Linux中使用gcc命令那样,也有其他的自动化模式帮我们不断在进行语法检查。

2.为什么要有库

库可以帮助我们提高开发效率,我们学习的时候提倡去造轮子,帮我们深入理解库的原理和实现方法。

到开发中,我们更提倡用轮子!

当然我们也可以选择将以提供源代码的方式,来提高开发效率,但是这对我们未来是由一些其他问题的比如我们不想让使用者看到我们的代码,比如我们的公司向另一个公司提供软件服务,我们只需要将打包好的库给他们使用即可,而不会把源码直接给他们!

3.写一写----从库的书写者的角度

库分为静态库(.a)动态库(.so)

另外一般云服务器,默认只会存在动态库,不会存在静态库,静态库需要独自安装!

3.1库的命名规则:

例如:libstdc++.so.6

去掉前面的lib和后面的第一个.后面包括.的内容结恶果就是库的名称了!

所以例子的这个库的名称使stdc++;

4.用一用----站在使用者的角度去使用我们写的库

4.1静态库

我们先简单实现一个加减函数的库:

我们先写四个文件:

//myadd.c  
#include"myadd.h"int myadd(int a, int b)
{return a + b;
}
//myadd.h
#pragma once
#include<stdio.h>int myadd(int a, int b);
//mysub.c
#include"mysub.h"
int mysub(int a, int b)
{return a - b;
}
//  mysub.h
#pragma once#include<stdio.h>int mysub(int a, int b);

首先我们之前的做法就是在写一个主函数的文件将他们一块编译:

但是这是我们只有这么两个源文件的时候看起来还可以,但是当我们的项目非常庞大的时候,并且我们需要分享给别人使用我们的库的时候,就非常的不方便了!

image-20231121165901487

我们将.c源文件都让其生成.o文件,也就是未链接前的文件:

利用ar命令生成静态库:ar -rc libmymath.a myadd.o mysub.o

image-20231121174308280

当我们把libmymath.a文件挪动到我们的ortherperson目录下的时,我们尝试编译的时候:

发现提示我们找不到头文件

image-20231121174153962

所以我们这样的方式是不对的!

我们先在ortherperson下创建两个文件夹libinclude分别存放生成的静态库和对应的头文件;

正确的是这样:gcc -o mytest main.c -I./include -L./lib -lmymath

我们来解释一下:

  • -I:其中I表示的inlcude的意思,也就是头文件的意思后面直接跟上我们头文件的路径
  • -L:其中L表示的Lib的意思,就是我们的库的意思,后面直接跟上我们静态库的路径
  • -l:这个参数后面紧跟我们的静态库的名字,就是我们前面命名规则说的那样,去掉前面的lib.后面的内容就是我们的静态库的名字!

image-20231121174529404

那么我们接下来尝试一下把我们的库放到语言库中直接去调尝试一下:

[root@lavm-5wklnbmaja ortherperson]# cp lib/libmymath.a /lib64
[root@lavm-5wklnbmaja ortherperson]# cp include/*.h /usr/include

尝试去编译:

我们发现编译成功,并且运行成功!

image-20231121180401518

所以我们这里就可以引入第三方库的使用:

①首先我们需要指定的头文件和库文件

②如果没有默认安装到系统gcc,g++默认的搜索路径下,用户必须指明对应的选项,告知编译器:

​ a.头文件在哪;

​ b.库文件在哪;

​ c.库文件具体是谁

③将我们下载下来的库和头文件,拷贝到系统默认路径下---- 在Linux下安装库!!那么卸载呢?对任何软件而言,安装和卸载的本质就是拷贝到系统特定的路径。

④如果我们安装的库是第三方的(语言,操作系统系统接口) 库,我们要正常使用,即便是已经全部安装到了系统中,gcc/g++必须要用-l指明具体库的名称!

理解现象:

​ 无论我们是从网络中未来直接下载好的库,或者是源代码(编译方法) — make install 安装的命令 --cp,安装到系统中,我们安装大部分指令,库等等都是需要sudo的或者超级用户操作!

4.2动态库

我们删除掉之前的只留下最开始的源文件和头文件:

我们重新生成.o文件:

[mi@lavm-5wklnbmaja mylib]$ gcc -c -fPIC -o myadd.o myadd.c
[mi@lavm-5wklnbmaja mylib]$ gcc -c -fPIC -o mysub.o mysub.c

其中:fPIC:产生位置无关码(position independent code)

image-20231121185348586

再生成动态库:

gcc -shared -o libmymath.so myadd.o mysub.o

image-20231121185748618

接下来我们尝试链接动态库:

gcc -o main main.c -Iinclude -Llib -lmymath

链接成功了,但是我们运行的时候却报错了,报错说无法找到我们的动态库文件!但是我们的链接却通过了。原因是我们确实把动态库的位置告诉了编译器,但是却没告诉操作系统!

image-20231121190107295

那么这里为什么没有成功呢?这里其实就是静态库和动态库的区别了,对于静态库将用户使用的二进制代码直接拷贝到可执行目标文件的程序中。但是我们的静态库不会。

5.动态库配置问题

运行时,OS是如何查找我们的动态库的?三种方法

5.1环境变量

动态库需要再一个环境变量LD_LIBRARY_PATH 中去寻找我们的动态库!(告诉操作系统动态库的位置)。

我们只需要把我们当前的动态库的路径添加到这个环境变量中即可:

image-20231121192516738

我们ldd查看一下链接的情况并且运行一下:

image-20231121192541623

当然这种方案我们不是很推崇是一种临时方案,原因是我们知道环境变量我们如果没有再配置文件修改的话,我们每次重新登录或者重启bash都会导致环境变量的初始化。

5.2软链接方案

sudo ln -s /learn/lesson11/ortherperson/lib/libmymath.so /lib64/libmymath.so

这个时候ldd我们再去查看的时候:

image-20231121193409826

并且我们这时候再去运行发现是没有任何问题的:

image-20231121193534934

并且对于软链接来说它不是临时的,就算我们重启bash也不会影响,因为他是真正写入到磁盘内部的,而不是在内存中!

5.3配置文件方案

首先我们查看一下系统的默认配置文件:

[mi@lavm-5wklnbmaja lesson11]$ ls /etc/ld.so.conf.d/
kernel-3.10.0-957.el7.x86_64.conf  mariadb-x86_64.conf

我们在这个目录下添加以一个我们的配置文件:xupt_lib.conf

//创建配置文件
[mi@lavm-5wklnbmaja lesson11]$ sudo touch /etc/ld.so.conf.d/xupt_lib.conf
//像配置文件中写入我们的动态库所在的绝对路径
[mi@lavm-5wklnbmaja lib]$ sudo vim /etc/ld.so.conf.d/xupt_lib.conf

我们ldd查看一下:

image-20231122150326397

发现似乎还是没有链接上,其实我们还少一步生效配置文件:

[mi@lavm-5wklnbmaja ortherperson]$ sudo ldconfig

运行一下看看:

image-20231122150654301

尝试运行:

image-20231122150722572

6.动态库加载过程的理解

静态链接形成的可执行程序中本身就有静态库中对应的方法实现!

首先我们的静态库存储在硬盘中,当多个可执行文件都要使用到这个库的时候,他就会加载到每个可执行程序内部。会让每个可执行程序的变大。其次因为它加载到了可执行程序内部,所以我们在外部删除这个静态库的时候我们的程序依然可以正常运行。所以主流会多使用动态库多一些:


6.1 动态库加载问题和周边问题:

链接:将可执行程序内部的外部符号,替换称为库中的具体地址。

只要我们把库加载到内存,映射到进程的地址空间中之后,我们的代码执行库中的方法,就依旧还是在自己的地址空间内进行函数跳转即可!

并且为什么动态库节省资源,原因是当多个进程都要使用到同一个动态库的时候,这个动态库只需要在内存加载一份,每个进程只需要在自己的进程地址空间中进行映射即可!因为我们曾经在链接动态库的时候程序内部已经存储了相关数据和函数的地址信息!

image-20231122154713731

有时候我们的库很大,我们的程序并不会用到其中所有的接口,所以我们的操作系统也没必要把库中所有的信息都加载到内存中。

6.2库中地址的理解

动态库必定面临一个问题:

不同的进程,运行的程度不同,需要使用的第三方库是不同的,注定了每一个共享空间中空闲位置是不确定的

在程序翻译链接,形成可执行程序的时候,程序内部有没有地址?

答案是有的:动态库中的地址,决定不能确定,所以不能使用绝对地址!动态库中的所有地址,都是偏移量(相对地址),默认从0开始。

当一个库,真正的被映射进地址空间的时候,它的起始地址才会被真正的确定!当我们映射的时候是将进程地址空间的内部的代码区包含库的相对地址+我们的动态库的初始地址,一块存储到进程地址空间对应的共享代码空间的位置!并且这个地址一旦被确定是不变的!所以我们经过这种方式就可以去访问动态库中的方法了!

所以动态库在我们的进程地址空间中随意加载,与我们在地址空间中加载的位置毫无关系!

所以这时候我们也可以理解之前,链接动态库的时候我们生成的汇编文件的时候带了一个参数-fPIC,被称作与位置无关码:也就是对应我们这里的相对地址,偏移量!

接下来我们验证一下:

我们重新生成一个非无关码的汇编文件:用于生成对应的静态库文件。

[mi@lavm-5wklnbmaja mylib]$ gcc -o myadd_static.o -c myadd.c
[mi@lavm-5wklnbmaja mylib]$ gcc -o mysub_static.o -c mysub.c
//生成对应的静态库
[mi@lavm-5wklnbmaja mylib]$ ar -rc libmymath.a myadd_static.o mysub_static.o 

将两个库拷贝到我们的ortherperson/lib目录下:

image-20231122171305250

接下来我们有一个问题,当静态库和动态库同时存在的情况下,我们的可执行文件会优先使用哪个呢?答案肯定是动态库

我们试验一下:重新编译文件并运行

image-20231122171700679

我们ldd查看一下可执行程序的链接情况:很显然是我们的动态库!

image-20231122171733025

那如果我们呢执意要静态链接呢?只需要再加一个参数-static

[mi@lavm-5wklnbmaja ortherperson]$ gcc -o mytest-s main.c -I./include -L./lib -lmymath -static

当我们尝试编译的时候报错了:这是因为我们没有安装对应的库:sudo yum install -y glibc-static

image-20231122183954762

我们分别在三种情况下编译:
①动态库和静态库都存在的情况生成:mytest-d

②动态库和静态库都存在的情况下加-static参数生成:mytest-s

③删除动态库只留下静态库再生成:mytest-u

ldd查看三个文件:

image-20231122184419491

所以我们可以得出结论:

①动态库和静态库同时存在,系统默认使用动态库

②编译器,在链接的时候,如果库提供了动和静,优先动,没有动,才使用静!

到这本篇博客的内容就到此结束了。
如果觉得本篇博客内容对你有所帮助的话,可以点赞,收藏,顺便关注一下!
如果文章内容有错误,欢迎在评论区指正

在这里插入图片描述

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

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

相关文章

iperf3 网络测试

iperf3 测试网络的上下行带宽 下载地址 https://iperf.fr/iperf-download.php 开启服务器 开启客户端 常用命令 -c 代表客户端-s 代表服务端-u 代表 udp-r 代表数据方向是否反向 https://baijiahao.baidu.com/s?id1731514357681464971&wfrspider&forpc

Python简直是万能的,这5大主要用途你一定要知道!

从2015开始国内就开始慢慢接触Python了&#xff0c;从16年开始Python就已经在国内的热度更高了&#xff0c;目前也可以算的上"全民Python"了。 众所周知小学生的教材里面已经有Python了&#xff0c;国家二级计算机证也需要学习Python了&#xff01; 因为Python简单…

2023全球边缘计算大会深圳站-核心PPT资料下载

一、峰会简介 边缘计算&#xff0c;是指在靠近物或数据源头的一侧&#xff0c;采用网络、计算、存储、应用核心能力为一体的开放平台&#xff0c;就近提供最近端服务。其应用程序在边缘侧发起&#xff0c;产生更快的网络服务响应&#xff0c;满足行业在实时业务、应用智能、安…

Linux中的进程程序替换

Linux中的进程程序替换 1. 替换原理2. 替换函数3. 函数解释4. 命名理解程序替换的意义 1. 替换原理 替换原理 用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的…

[Docker]九.Docker compose讲解

docker-compose 是 docker 官方的一个开源项目&#xff0c;可以实现对 docker 容器集群的快速编排, docker-compose 通过一个 配置文件 来管理多个 Docker 容器,在配置文件中&#xff0c;所有的容器通过 services 来定义&#xff0c;然后使用 docker-compose脚本 来 启动&am…

Nuxt.js Next.js Nest.js

Nuxt.js和Next.js都是服务端渲染框架(SSR)&#xff0c;属于前端框架,Nest.js则是node框架,属于后端框架。 其中Nuxt.js是vue的ssr框架&#xff0c;Next.js是react的ssr框架。 都是比vue和react更上层的前端框架。 文章目录 1.SSR2.Nuxt2.1 Nuxt的下载2.2 Nuxt的集成2.3 Nuxt…

HuggingFace-利用BERT预训练模型实现中文情感分类(下游任务)

准备数据集 使用编码工具 首先需要加载编码工具&#xff0c;编码工具可以将抽象的文字转成数字&#xff0c;便于神经网络后续的处理&#xff0c;其代码如下&#xff1a; # 定义数据集 from transformers import BertTokenizer, BertModel, AdamW # 加载tokenizer token Ber…

langchain 部署组件-LangServe

原文&#xff1a;&#x1f99c;️&#x1f3d3; LangServe | &#x1f99c;️&#x1f517; Langchain LangServe &#x1f6a9; We will be releasing a hosted version of LangServe for one-click deployments of LangChain applications. Sign up here to get on the wa…

OpenLayers入门,OpenLayers6的WebGLPointsLayer图层样式和运算符详解,四种symbolType类型案例

专栏目录: OpenLayers入门教程汇总目录 前言 本章讲解使用OpenLayers6的WebGL图层显示大量点情况下,列举出所有WebGLPointsLayer图层所支持的所有样式运算符大全。 补充说明 本篇主要介绍OpenLayers6.x版本的webgl图层,OpenLayers7.x和OpenLayers8.x主要更新内容就是webgl…

GB28181学习(十七)——基于jrtplib实现tcp被动和主动发流

前言 GB/T28181-2022实时流的传输方式介绍&#xff1a;https://blog.csdn.net/www_dong/article/details/134255185 基于jrtplib实现tcp被动和主动收流介绍&#xff1a;https://blog.csdn.net/www_dong/article/details/134451387 本文主要介绍下级平台或设备发流功能&#…

生活如果真能像队列一样的话

生活如果真能像队列一样&#xff0c;那该多好啊。 —————————————————————————————————————————— 背包&#xff0c;队列 可以先看他们的API&#xff1a;都含有一个无参构造函数&#xff0c;添加单个元素的方法&#xff0c;测试集合…

基于材料生成算法优化概率神经网络PNN的分类预测 - 附代码

基于材料生成算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于材料生成算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于材料生成优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

ROSNS3(一)

https://github.com/malintha/rosns3 第一步&#xff1a;clone和构建rosns3客户端 第二步&#xff1a;运行 最详细的ubuntu 安装 docker教程 - 知乎 1. unable to find source space /home/muta/src 解决方法&#xff1a; 将副将将碰到的bug&#xff0c;解决方法_#include &…

Python从零开始快速搭建一个语音对话机器人

文章目录 01-初心缘由02-准备工作03-语音机器人的搭建思路04-语音生成音频文件05-音频文件转文字STT06-与图灵机器人对话07-文字转语音08-语音对话机器人的完整代码09-结束语10-有问必答关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学…

SSH连接远程服务器报错:WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED 解决方法

一.错误描述 报错信息里提示了路径信息/root/.ssh/known_hosts:20 二.解决方案 方法一 输入以下指令&#xff1a; ssh-keygen -R XXX&#xff08;需要连接远程服务器的ip&#xff09; 按照我的例子ip:10.165.7.136&#xff0c;会返回以下信息: 重新尝试连接&#xff1a; 输…

规划类3d全景线上云展馆帮助企业轻松拓展海外市场

科技3D线上云展馆作为一种基于VR虚拟现实和互联网技术的新一代展览平台。可以在线上虚拟空间中模拟真实的展馆&#xff0c;让观众无需亲自到场&#xff0c;即可获得沉浸式的参观体验。通过这个展馆&#xff0c;您可以充分、全面、立体展示您的产品、服务以及各种创意作品&#…

2023年【安全生产监管人员】考试题及安全生产监管人员找解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 安全生产监管人员考试题参考答案及安全生产监管人员考试试题解析是安全生产模拟考试一点通题库老师及安全生产监管人员操作证已考过的学员汇总&#xff0c;相对有效帮助安全生产监管人员找解析学员顺利通过考试。 1、…

数据结构-栈的实现

1.栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last In First Out&#xff09;的原则。 压栈&…

Matlab群体智能优化算法之海象优化算法(WO)

文章目录 一、灵感来源二、算法的初始化三、GTO的数学模型Phase1&#xff1a;危险信号和安全信号Phase2&#xff1a;迁移&#xff08;探索&#xff09;Phase3&#xff1a;繁殖&#xff08;开发&#xff09; 四、流程图五、伪代码六、算法复杂度七、WO搜索示意图八、实验分析和结…