【从浅学到熟知Linux】基础IO第四弹=>动静态库(含第三方动静态的使用、自制动静态库、关于动静态库加载调用原理)

在这里插入图片描述

🏠关于专栏:Linux的浅学到熟知专栏用于记录Linux系统编程、网络编程等内容。
🎯每天努力一点点,技术变化看得见

文章目录

  • 静态库
    • 静态库的介绍及使用方法
    • 自制静态库
    • 使用第三方提供的静态库
  • 动态库
    • 动态库的介绍及使用方法
    • 自制动态库
    • 第三方动态库的使用
  • 动静态库的加载与调用原理


静态库

静态库的介绍及使用方法

在Linux系统中,静态库以.a后缀结尾。我们可以进入/lib64目录下,使用ls -al | grep "\.a$"查找当前目录下的静态库。↓↓↓

在这里插入图片描述
静态库的名称需要去掉前面的lib及后缀.a。如下图所示,该静态库的名称为util↓↓↓
在这里插入图片描述

程序在编译链接的时候把静态库库的代码链接到可执行文件中,程序运行的时候不再需要静态库。例如,我们需要使用libutil.a中的代码,在编译链接的时候,就将该库中的代码拷贝到待生成程序的代码中。
在这里插入图片描述
当使用静态链接生成的可执行程序被执行时,由于该程序已经包含了静态库中的代码,当其执行时就不再需要使用静态库,而是直接在该程序的正文代码段寻找对应的库代码即可。
在这里插入图片描述

gcc/g++编译器在默认情况下,都采用动态链接,只有用户在编译链接时显式带上-static选项才会采用静态链接。我们对下方程序分别采用动态链接及静态链接↓↓↓

#include <stdip.h>int main()
{printf("Jammingpro\n");return 0;
}

在这里插入图片描述
从上面的文件大小可以看出,静态链接的程序大小明显大于动态链接的程序大小。因为静态链接将使用到的库的可执行程序代码保存到其代码中,导致可执行程序的大小比较大。

自制静态库

在市面上,如果购买一个第三方库,我们将得到包含该库中的各个方法声明的头文件,及这些方法对应的实现生成的可执行程序(为了不让其他人知道这个方法的具体实现,故已经将这些方法的实现编译成可执行程序)。

下面,我们自制一个包含加减乘除函数的mymath静态库。我们需要包含一个mymath静态库的头文件,一个包含具体实现的mymath静态库。最终组成lib目录下保存include和mymathlib文件夹,这两文件夹内分别包含头文件及静态库↓↓↓
在这里插入图片描述

首先,我们需要提供包含各个函数声明的头文件(mymath.h)↓↓↓

#ifndef __MYMATH_H__
#define __MYMATH_H__int add(int left, int right);int sub(int left, int right);int mul(int left, int right);int div(int left, int right);#endif

下面是上方各个函数声明的具体实现代码(mymath.c)↓↓↓

#include "mymath.h"//下方代码实现不考虑除零错误及超出int表示范围的情况int add(int left, int right)
{return left + right;
}int sub(int left, int right)
{return left - right;
}int mul(int left, int right)
{return left * right;
}int div(int left, int right)
{return left / right;
}

对于上面的mymath.c源文件,我们需要使用gcc -c mymath.c生成与源文件同名的目标文件mymath.o。

在继续做下一步前,先介绍一个命令ar -rc,该命令用于创建归档文件,其中-c表示创建归档文件,-r表示将指定文件放入归档文件中。

所以,接下来,我们可以将mymath.o加入到libmymath.a的归档文件中,即我们需要执行ar -rc mymath.o libmymath.a

最后,我们需要在创建lib、include、mymathlib文件夹,并将头文件及生成的静态库文件拷贝到指定位置即可↓↓↓

mkdir -p ./lib/include
mkdir -p ./lib/mymathlib
cp *.h ./lib/include
cp *.a ./lib/mymathlib

执行完上述操作后,我们就制作完属于自己的静态库了!

使用第三方提供的静态库

当我们下载某个库时,本质就是将对应的库拷贝到我们的计算机中。下面,我们使用在当前目录中创建user目录,来模拟用户下载和使用我们编写的静态库↓↓↓

下图,将lib目录拷贝到user目录下,模拟用户下载第三方静态库↓↓↓
在这里插入图片描述
下面,编写一个程序main.c,在该程序中调用我们编写静态库↓↓↓

#include <stdio.h>
#include "mymath.h"int main()
{printf("2 + 1 = %d\n", add(1, 1));printf("2 - 1 = %d\n", sub(1, 1));printf("2 * 1 = %d\n", mul(1, 1));printf("2 / 1 = %d\n", div(1, 1));return 0;
}

gcc/g++会在指定目录中查找静态库及其头文件,由于我们自定义的静态库并没有在指定目录中,故我们对main.c进行编译时会出错↓↓↓
在这里插入图片描述
当我们使用gcc [源代码文件名] -I [头文件目录] -L [库文件目录] 指名使用的库的头文件位置,库文件位置时,链接程序时仍会报错↓↓↓
在这里插入图片描述
除了需要指名头文件和库文件的位置,我们还需要指明使用的是哪一个库,即使用-l[库名称]指名使用的库的名称。↓↓↓
在这里插入图片描述
至此,我们自定义的静态库就能够被使用了↓↓↓
在这里插入图片描述

动态库

动态库的介绍及使用方法

在Linux系统中,动态库以.so后缀结尾。我们可以进入/lib64目录下,使用ls -al | grep "\.so$"查找当前目录下的动态库。↓↓↓
在这里插入图片描述
动态库的名称需要去掉前面的lib及后缀.so。如下图所示,该动态库的名称为c↓↓↓
在这里插入图片描述

以动态链接生成的可执行文件,在被执行时,会将指定的库文件加载到内存中。并将使用到的动态库代码地址存储于程序地址空间的共享区中。当正文代码段执行到需要调用动态库的代码时,会跳转到共享区中寻找对应代码的地址,通过页表映射到物理地址,并执行对应的代码。
在这里插入图片描述

gcc/g++编译器在默认情况下,都采用动态链接,我们可以使用ldd查看程序的动态库链接情况(对于静态链接的程序,ldd会提示该程序不是动态链接)↓↓↓
在这里插入图片描述

自制动态库

与制作静态库类似,我们需要提供对应方法的声明(头文件)和实现(.o后缀的目标文件)。但动态库在生成.o的目标文件及生成库的最后一步时,与静态库制作存在差异。

制作的动态库的最终效果如下,lib目录下包含include和mymathlib两个目录,两目录分别包含头文件及动态库文件。↓↓↓
在这里插入图片描述

一样的,首先,我们需要提供包含各个函数声明的头文件(mymath.h)↓↓↓

#ifndef __MYMATH_H__
#define __MYMATH_H__int add(int left, int right);int sub(int left, int right);int mul(int left, int right);int div(int left, int right);#endif

下面是上方各个函数声明的具体实现代码(mymath.c)↓↓↓

#include "mymath.h"//下方代码实现不考虑除零错误及超出int表示范围的情况int add(int left, int right)
{return left + right;
}int sub(int left, int right)
{return left - right;
}int mul(int left, int right)
{return left * right;
}int div(int left, int right)
{return left / right;
}

但在生成.o的目标文件时,需要带上-fPIC(用于产生与位置无关码),即gcc -c -fPIC main.c。为什么带上-fPIC选项将于下文介绍。

所以,接下来生成动态库的操作与静态存在差异,动态库并不使用归档方式,而是使用gcc -shared -o [动态库名称] [目标文件名.o],即使用gcc -shared -o libmymath.so mymath.o

最后,我们需要在创建lib、include、mymathlib文件夹,并将头文件及生成的动态库文件拷贝到指定位置即可↓↓↓

mkdir -p ./lib/include
mkdir -p ./lib/mymathlib
cp *.h ./lib/include
cp *.a ./lib/mymathlib

执行完上述操作后,我们就制作完属于自己的动态库了!

第三方动态库的使用

我们在当前目录下创建user目录,模拟用户使用动态库的过程↓↓↓
在这里插入图片描述
下载的本质就是拷贝,我们将自定义的动态库拷贝到user目录下↓↓↓
在这里插入图片描述
下面,编写一个程序main.c,在该程序中调用我们编写动态库↓↓↓

#include <stdio.h>
#include "mymath.h"int main()
{printf("2 + 1 = %d\n", add(1, 1));printf("2 - 1 = %d\n", sub(1, 1));printf("2 * 1 = %d\n", mul(1, 1));printf("2 / 1 = %d\n", div(1, 1));return 0;
}

有了静态库的使用经验,我们知道了需要告诉编译头文件、库文件位置,及使用的库文件名↓↓↓
在这里插入图片描述
但我们在运行可执行文件时,会报错:找不到对应的动态库↓↓↓
在这里插入图片描述
由于我们在编译链接时,告诉编译器库的头文件和库的位置,及使用的库文件名;但执行时,动态库代码并没有在可执行文件中,需要将对应的动态库代码加载到物理内存并映射入当前进程的共享区中。由于系统只会查找默认路径下的动态库,由于默认路径下的没有mymath动态库,故报错。

下面给出4中解决加载不到动态库的方法↓↓↓

  1. 由于系统默认会在/lib64目录下查找动态库,故我们可以将动态库保存在/lib64目录下↓↓↓

在这里插入图片描述

  1. 在系统默认路径下,即/lib64目录下,建立对应动态库的软链接↓↓↓

在这里插入图片描述
在这里插入图片描述

  1. 将自己的库所有的路径添加到系统的环境变量LD_LIBRARY_PATH中,系统在搜索动态库时,会在该环境变量中的路径中查找(export设置的环境变量仅在当前会话中有效,可以修改环境变量配置文件来永久修改环境变量的值)↓↓↓

在这里插入图片描述

  1. 在/etc/ld.so.conf.d目录中建立自己的动态库路径的配置文件,文件名称只需要以.conf即可,文件内容保存的就是我们的动态库存储位置;在创建配置文件后,需要再使用lfconfig,使得配置文件生效(这些操作均需要root权限)↓↓↓

在这里插入图片描述
在这里插入图片描述

动静态库的加载与调用原理

静态链接中,静态库中的代码被编址到程序的正文代码段中,静态库的代码称为了可执行程序代码的一部分。虽然在执行程序时不需要再链接库文件,但重复存储库代码造成了极大的浪费。例如:我们的各个程序基本都需要使用到C语言库,如果每个程序运行都要加载1份C库代码,则内存中会存在大量相同的代码。(静态链接原理在上文以有介绍)

而动态链接的代码段中并不保存库代码,而是等程序运行的时候才去链接动态库的代码,多个程序共享相同的库代码。这样可以大大提高内存的使用效率,提高代码的复用率。

一个以动态库链接的可执行文件仅仅包括包含它用到的函数的入口函数的一个表,而不是外部函数所在目标文件的整个机器码。在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,当前程序需要使用某个函数时,通过入口函数表找到对应的函数执行即可,这个过程称为动态链接。

关于动态链接,下面再做更详细的介绍:

动态链接时,在当前程序的虚拟地址空间的共享区中会保存对应动态库代码的起始地址及各个函数相对起始地址的偏移量。↓↓↓
在这里插入图片描述
上图中,C动态库的起始地址可以通过共享区的地址,经过页表映射找到物理地址0x11111111。当正文代码段需要调用printf函数时,会先找到C动态库在物理地址的起始存储处;然后在共享区存储的入口函数表查到printf相对于起始的地址的偏移量是8,故printf的物理地址为0x11111119。

对于静态库代码中,它的内部可以存储虚拟地址;但对于动态库代码来说,它的内部不能存储虚拟地址,而应该存储相对于起始地址的偏移量,故编译时需要带上-fPIC(与位置无关码)选项。

动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。

🎈欢迎进入从浅学到熟知Linux专栏,查看更多文章。
如果上述内容有任何问题,欢迎在下方留言区指正b( ̄▽ ̄)d

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

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

相关文章

大数据Spark--运行环境和架构

文章目录 Spark运行环境Local模式解压缩文件启动 Local 环境命令行工具退出本地模式提交应用 Standalone 模式解压缩文件修改配置文件启动集群提交应用提交参数说明配置历史服务配置高可用&#xff08;HA Yarn模式解压缩文件修改配置文件启动HDFS 以及YARN集群配置历史服务器 K…

List的介绍

前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; hellohello~&#xff0c;大家好&#x1f495;&#x1f495;&#xff0c;这里是E绵绵呀✋✋ &#xff0c;如果觉得这篇文章还不错的话还请点赞❤️❤️收藏&#x1f49e; &#x1f49e; 关注&#x1f4a5;&#x1…

python-自动化篇-终极工具-用GUI自动控制键盘和鼠标-pyautogui-键盘

文章目录 键盘键盘——记忆宫殿入门——通过键盘发送一个字符串——typewrite()常规——键名——typewrite()常规——按下键盘——keyDown()常规——释放键盘——keyUp()升级——热键组合——hotkey() 键盘 pyautogui也有一些函数向计算机发送虚拟按键&#xff0c;让你能够填充…

LeetCode-电话号码的字母组合(回溯)

每日一题 今天刷到的是一道利用回溯来解决的题&#xff0c;不过稍微有点复杂&#xff0c;并且我也有一段时间没有做回溯了&#xff0c;所有在解题时也是思考了一段时间。 题目要求 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意…

vue实现文字转语音的组件,class类封装,实现项目介绍文字播放,不需安装任何包和插件(2024-04-17)

1、项目界面截图 2、封装class类方法&#xff08;实例化调用&#xff09; // 语音播报的函数 export default class SpeakVoice {constructor(vm, config) {let that thisthat._vm vmthat.config {text: 春江潮水连海平&#xff0c;海上明月共潮生。滟滟随波千万里&#xf…

【每日刷题】Day20

【每日刷题】Day20 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 面试题 17.04. 消失的数字 - 力扣&#xff08;LeetCode&#xff09; 2. 189. 轮转数组 - 力扣&#…

AppWizard的软件开发GUI的使用记录

前言 这个软件是针对于EmWin6.0以上的这个软件在emWin的基础上又封装了一层,也只提供的API函数.基于消息事件为核心&#xff08;个人理解&#xff09;一些组件的之间的交互可以通过软件界面进行配置,比较方便本次是基于模拟器进行测试记录,观察api 按键和文本之间的关联 通过…

Crypto量化高频体验总结

Crypto量化高频体验总结 人工智能与量化交易算法知识库 2024-04-21 21:02 美国 以下文章来源于Quant搬砖工 &#xff0c;作者quant搬砖队工头 Quant搬砖工. 稳健的收益要一点一点赚&#xff0c;量化的板砖要一块一块搬&#xff01; 前言 前两天在翻历史文章的时候&#xf…

Linux--Linux命令使用技巧

Linux命令使用技巧 1、tab键自动补全2、连续连tab键&#xff0c;给出操作提示3、使用上下箭头可以调出曾经使用过的命令4、使用clear命令或者ctrll快捷键实现清屏 1、tab键自动补全 2、连续连tab键&#xff0c;给出操作提示 3、使用上下箭头可以调出曾经使用过的命令 4、使用…

【树莓派】如何刷个系统给树莓派4B,如何ssh登陆到树莓派

文章目录 下载树莓派镜像下载烧写软件烧写编辑设置连接树莓派4B重启ssh查看树莓派IPssh远程连接问询、帮助 下载树莓派镜像 https://www.raspberrypi.com/software/operating-systems/#raspberry-pi-os-64-bit 下载烧写软件 https://www.raspberrypi.com/software/ 烧写 编辑…

拿捏 顺序表(1)

目录 1. 顺序表的分类2. 顺序表实现3. 顺序表实现完整代码4. 总结 前言: 一天xxx想存储一组数据, 并且能够轻松的实现删除和增加, 此时数组大胆站出, 但是每次都需要遍历一遍数组, 来确定已经存储的元素个数, 太麻烦了, 于是迎来了顺序表不屑的调侃: 数组你不行啊… 顺序表是一…

FairAdaBN论文速读

FairAdaBN: Mitigating Unfairness with Adaptive Batch Normalization and Its Application to Dermatological Disease Classification 摘要 深度学习在医疗研究和应用中变得越来越普遍&#xff0c;同时涉及敏感信息和关键诊断决策。研究人员观察到不同人口统计属性子组之间…