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

news2025/1/16 2:42:30

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

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

🌴专栏链接: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.coloradmin.cn/o/1239893.html

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

相关文章

Linux 局域网传输工具LANDrop安装

Linux 局域网传输工具LANDrop安装 &#x1f959;下载&#x1f32d;解压&#x1f96a;运行 &#x1f959;下载 官网下载 或网盘 &#x1f32d;解压 使用以下命令解压获得squashfs-root文件夹 ./LANDrop-latest-linux.AppImage --appimage-extract&#x1f96a;运行 进入squ…

分布式锁3: zk实现分布式锁

一 zk 实现分布式锁 1.1 zk分布式操作命令 1.指令&#xff1a; ls / get /zookeeper create /aa "test" delete /aa set /aa "test1" 2..znode节点类型&#xff1a; 永久节点&#xff1a;create /pa…

028 - STM32学习笔记 - ADC结构体学习(二)

028 - STM32学习笔记 - 结构体学习&#xff08;二&#xff09; 上节对ADC基础知识进行了学习&#xff0c;这节在了解一下ADC相关的结构体。 一、ADC初始化结构体 在标准库函数中基本上对于外设都有一个初始化结构体xx_InitTypeDef&#xff08;其中xx为外设名&#xff0c;例如…

从mysql源码编译出相应的库和可执行文件及搭建mysql服务端

目录 1. 问题的提出 2. 源码下载 3. 升级或安装某些前置软件 3.1. 升级CMake 3.2. 升级gcc、g 4. 安装依赖库 4.1. 安装OpenSSL 4.2. 安装Curses 4.3. 安装pkg-config 5. 编译、安装 6. 编译结果、配置 7. 编译错误处理 7.1. 错误1 7.2. 错误2 8. 搭建mysql数…

FindMy技术用于充电宝

充电宝是一种便捷的充电器&#xff0c;方便个人随身携带&#xff0c;能够自行储备电能&#xff0c;为主流电子设备提供充电服务。它广泛应用于没有外部电源供应的场所&#xff0c;例如旅行、户外活动或紧急情况下&#xff0c;为用户的手持设备提供持续的电力支持&#xff0c;确…

C语言--数组与指针--打印字符串的n种方式

一.知识背景 一维数组名的含义 arr一般表示数组的起始地址&#xff08;除了两种例外&#xff09; 1.在定义数组的同一个函数中(不是形参),求sizeof(arr),求整个数组的字节数 2.在定义数组的同一个函数中(不是形参),&arr1,加整个数组的大小 (经常考试) 3.除上面以外,arr都表…

第四代智能井盖传感器:智能井盖位移怎么进行监测

井盖是城市基础设施的一个重要组成部分&#xff0c;若井盖出现移位等现象&#xff0c;可能会对路过的车辆和行人造成潜在危险。特别是那些含有甲烷气体的井盖&#xff0c;一旦气体超过阈值且被意外踩踏&#xff0c;可能会导致气体的释放&#xff0c;这便会引发一系列安全事故&a…

给定一个非严格递增排列的有序数组,删除数组中的重复项

实例要求&#xff1a;1、给定一个非严格递增排列的有序数组 nums &#xff1b;2、原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff1b;3、返回删除后数组的新长度&#xff1b;4、元素的 相对顺序 应该保持 一致 &#xff1b;5、然后返回 nums 中唯一元素的…

Unsupervised MVS论文笔记

Unsupervised MVS论文笔记 摘要1 引言2 相关工作3 实现方法 Tejas Khot and Shubham Agrawal and Shubham Tulsiani and Christoph Mertz and Simon Lucey and Martial Hebert. Tejas Khot and Shubham Agrawal and Shubham Tulsiani and Christoph Mertz and Simon Lucey and …

二十二、数组(4)

本章概要 随机生成泛型和基本数组 随机生成 我们可以按照 Count.java 的结构创建一个生成随机值的工具&#xff1a; Rand.java import java.util.*; import java.util.function.*;import static com.example.test.ConvertTo.primitive;public interface Rand {int MOD 10_0…

详解自动化之单元测试工具Junit

目录 1.注解 1.1 Test 1.2 BeforeEach 1.3 BeforeAll 1.4 AfterEach 1.5 AfterAll 2. 用例的执行顺序 通过 order() 注解来排序 3. 参数化 3.1 单参数 3.2 多参数 3.3 多参数(从第三方csv文件读取数据源) 3.4 动态参数ParameterizedTest MethodSource() 4. 测试…

数字IC后端设计利器 - 《Innovus的基本使用流程和命令》

Innovus作为数字后端工具的后起之秀&#xff0c;在先进工艺下已经取得了令人瞩目的成就。其在Run time上的优势令人刮目&#xff0c;在timing、DRC、IR-drop上的结果更是让人竖起大拇指。 数字IC后端工程师学习Cadence公司的Innovus工具非常重要&#xff0c;因为Innovus是一款…

YOLOv8改进实战 | 更换主干网络Backbone(六)之轻量化模型VanillaNet进阶篇

前言 轻量化网络设计是一种针对移动设备等资源受限环境的深度学习模型设计方法。下面是一些常见的轻量化网络设计方法: 网络剪枝:移除神经网络中冗余的连接和参数,以达到模型压缩和加速的目的。分组卷积:将卷积操作分解为若干个较小的卷积操作,并将它们分别作用于输入的不…

封面从这里取好啦

文章目录 前端NPMViteNode.js 后端JavaMavenPython 数据库算法 前端 NPM Vite Node.js 后端 Java Maven Python 数据库 算法

常见面试题-Redis持久化策略

谈谈Redis 的持久化策略&#xff1f; 参考文章&#xff1a; Redis 持久化机制演进与百度智能云的实践 Redis的确是将数据存储在内存的&#xff0c;但是也会有相关的持久化机制将内存持久化备份到磁盘&#xff0c;以便于重启时数据能够重新恢复到内存中&#xff0c;避免数据丢…

反编译-ApkTool

ApkTool下载地址&#xff1a; Apktool | ApktoolA tool for reverse engineering Android apk fileshttps://apktool.org/ 1、使用 apktool 解包 执行 java -jar apktool_2.4.1.jar d demo.apk -o demo 命令 java -jar apktool_2.4.1.jar d demo.apk -o demo 其中 d 后面是…

嵌入式基础知识学习:Flash、EEPROM、RAM、ROM

https://blog.csdn.net/y673533511/article/details/87913989 FLASH存储器又称闪存&#xff0c;它结合了ROM和RAM的长处&#xff0c;不仅具备电子可擦出可编程(EEPROM) 的性能&#xff0c;还不会断电丢失数据同时可以快速读取数据 (NVRAM 的优势)&#xff0c;U 盘和MP3 里用的…

JSP:Javabean

起初&#xff0c;JavaBean的目的是为了将可以重复使用的代码进行打包&#xff0c;在传统的应用中&#xff0c;JavaBean主要用于实现一些可视化界面&#xff0c;如一个窗体、按钮、文本框等&#xff0c;这样的JavaBean称之可视化的JavaBean。 随着技术的不断发展与项目的需求&am…

MySQL 事务的底层原理和 MVCC(一)

在事务的实现机制上&#xff0c;MySQL 采用的是 WAL&#xff08;Write-ahead logging&#xff0c;预写式日志&#xff09;机制来实现的。 在使用 WAL 的系统中&#xff0c;所有的修改都先被写入到日志中&#xff0c;然后再被应用到系统中。通常包含 redo 和 undo 两部分信息。 …

react大文件上传

目录 大文件上传优点&#xff1a; 大文件上传缺点: 大文件上传原理&#xff1a; 为什么要用md5 实现流程&#xff1a; 部分代码1&#xff1a; 部分代码2&#xff1a;​ 大文件上传优点&#xff1a; 文件太大分片上传能加快上传速度,提高用户体验能断点续传 如果上次上传失败…