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

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

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

🌴专栏链接: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.hqwc.cn/news/207813.html

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

相关文章

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…

关于用css设置input输入框hover的时候的样式以及当input为disabled的时候,不要让hover样式生效

效果如果&#xff1a; 编辑状态下的时候&#xff1a; 只读状态下的时候&#xff1a; 代码如图&#xff1a; <input type"text" name"dataForm.exportCode" id"exportCodeItem" required :disabled"editDisabled" />input:not(…

HTML玩转超链接a标签

大家应该都知道&#xff0c;a标签主要是转跳链接&#xff0c;接下来&#xff0c;让我为大家介绍一下a标签的使用&#xff01; 主要的作用&#xff1a;从当前页面进行跳转 标签名标签语义常用属性单/双标签a超链接href&#xff1a;要跳转的具体位置 target&#xff1a;跳转时如…

Spark的通用运行流程与Spark YARN Cluster 模式的运行流程

Spark的通用运行流程 集群启动后Worker节点会向Master节点心跳汇报资源Client向Driver提交APP&#xff0c;根据不同的运行模式在不同的地方创建Driver。Driver以粗粒度的方式向Master注册应用并申请资源&#xff08;在Application执行之前&#xff0c;将所有的资源申请完毕&…

[操作系统]进程和线程

目录 1.什么是进程 1.1进程控制块抽象 1.2 CPU 分配 —— 进程调度&#xff08;Process Scheduling&#xff09; 1.3内存分配 —— 内存管理&#xff08;Memory Manage&#xff09; 1.4进程间通信(Inter Process Communication) 2.线程 2.1概念 2.2为什么要有线程 2.3线…

阿里巴巴国际站自养号测评方式提升星级的优势与要求

作为国际站的商家&#xff0c;都明白星等级这个重要指标的重要性。它像一块闪耀的招牌&#xff0c;不仅彰显了商家的运营能力&#xff0c;还体现了公司的实力。维护一个高等级的星等级&#xff0c;可以使商家的运营工作更加轻松&#xff0c;而低等级则需要花费更多的精力。提升…

TikTok Shop订单狂涨,黑五全托管品类日卖爆了

01 黑五品类日爆单 显然&#xff0c;TikTok Shop在美国的首个黑五大促收获了胜利的果实。 根据最新发布的数据&#xff0c;TikTok Shop全托管黑五六大品类日支付GMV&#xff08;总交易额&#xff09;和支付量双双实现大幅度增长。举其中几个具体数据来看&#xff0c;女装童鞋…

redis运维(十五) 集合

一 集合 ① 概念 集合的元素在redis里面的世界是member集合&#xff1a; setset集合当中不允许重复的元素&#xff0c;而且set集合当中元素是没有顺序的,不存在元素下标 ② sadd、smembers、srem ③ sismember、srandmember、spop、scard spop 命令用于移除集合中的指定 …

【Django-DRF】多年md笔记第5篇:Django-DRF的Request、Response和视图详解

本文从分析现在流行的前后端分离Web应用模式说起&#xff0c;然后介绍如何设计REST API&#xff0c;通过使用Django来实现一个REST API为例&#xff0c;明确后端开发REST API要做的最核心工作&#xff0c;然后介绍Django REST framework能帮助我们简化开发REST API的工作。 Dj…

人工智能基础部分22-几种卷积神经网络结构的介绍,并用pytorch框架搭建模型

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能基础部分22-几种卷积神经网络结构的介绍&#xff0c;本篇文章我将给大家详细介绍VGG16、VGG19、ResNet、SENet、MobileNet这几个卷积神经网络结构&#xff0c;以及pytorch搭建代码&#xff0c;利用通用数据…

【tomcat】java.lang.Exception: Socket bind failed: [730048

项目中一些旧工程运行情况处理 问题 1、启动端口占用 2、打印编码乱码 ʮһ&#xfffd;&#xfffd; 13, 2023 9:33:26 &#xfffd;&#xfffd;&#xfffd;&#xfffd; org.apache.coyote.AbstractProtocol init &#xfffd;&#xfffd;&#xfffd;&#xfffd;: Fa…

基于python人脸性别年龄检测系统-深度学习项目

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介简介技术组成1. OpenCV2. Dlib3. TensorFlow 和 Keras 功能流程 二、功能三、系统四. 总结 一项目简介 # Python 人脸性别年龄检测系统介绍 简介 该系统基…