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

news2025/1/13 7:26:00

在这里插入图片描述

🏠关于专栏: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.coloradmin.cn/o/1623617.html

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

相关文章

react之初识state

第二章 - 添加交互 State: 组件的记忆 组件通常需要根据交互更改屏幕上显示的内容。输入表单应该更新输入字段&#xff0c;单击轮播图上的“下一个”应该更改显示的图片&#xff0c;单击“购买”应该将商品放入购物车。组件需要“记住”某些东西&#xff1a;当前输入值、当前…

Linux操作系统的安装与配置

目录 (1)实验目的&#xff1a; (2)实验内容&#xff1a; (3)实验原理&#xff1a; (4)实验步骤&#xff1a; 1.先下载vmware workstation pro软件&#xff0c;下载地址:https://www.vmware.com/products/workstation-pr o/workstation-pro-evaluation.html 2.下载完成后&…

使用 pytorch训练自己的图片分类模型

如何自己训练一个图片分类模型&#xff0c;如果一切从头开始&#xff0c;对于一般公司或个人基本是难以实现的。其实&#xff0c;我们可以利用一个现有的图片分类模型&#xff0c;加上新的分类&#xff0c;这种方式叫做迁移学习&#xff0c;就是把现有的模式知识&#xff0c;转…

重要提醒!别再这样搭建帮助中心系统了

你们有没有这样的经历呢&#xff1f;当你使用某产品或服务时遇到问题&#xff0c;打开产品或服务的帮助中心&#xff0c;但界面设计太复杂&#xff0c;内容搜出来的内容多但是混乱不一致。或者更糟糕的是&#xff0c;帮助中心的界面设计看得人眼花缭乱。 所以&#xff0c;反思一…

全长直线度的检查方法和设备

关键字:全长直线度, 直线度测量仪,直线度测量机,直线度检测,直线度检测设备, 全长直线度的检测是确保机械部件、导轨、机床工作台等在全长范围内直线运动精度的重要手段。以下是一些常用的全长直线度检测方法和设备&#xff1a; --------直角尺和水平仪--------&#xff1a;…

bit、进制、位、时钟(窗口)、OSI七层网络模型、协议、各种码

1.bit与进制 &#xff08;个人理解&#xff0c;具体电路是非常复杂的&#xff09; 物理层数据流&#xff0c;bit表示物理层数据传输单位&#xff0c; 一个电路当中&#xff0c;通过通断来表示数字1和0 两个电路要通讯&#xff0c;至少要两根线&#xff0c;一根作为电势参照…

浓眉大眼的Apple开源OpenELM模型;IDM-VTON试衣抱抱脸免费使用;先进的语音技术,能够轻松克隆任何人的声音

✨ 1: openelm OpenELM是苹果机器学习研究团队发布的高效开源语言模型家族 OpenELM是苹果机器学习研究团队开发的一种高效的语言模型&#xff0c;旨在推动开放研究、确保结果的可信赖性、允许对数据和模型偏见以及潜在风险进行调查。其特色在于采用了一种分层缩放策略&#x…

定时器介绍

定时器简介 一、周期定时功能二、PWM功能三、脉冲捕获四、事件计数五、扩展触发功能 一、周期定时功能 定时器的时钟为所选时钟源LRC、OSC、HRC、PLL通过定时器内的预分频器TMRDIV分频得到。 二、PWM功能 包括向上、下、中央计数方式&#xff0c;以向上计数为例计数和引脚产生…

使用excel文件生成sql脚本

目录 1、excel文件脚本变量2、公式示例 前言&#xff1a;在系统使用初期有一些基础数据需要从excel中导入到数据库中&#xff0c;直接导入的话可能有些字段用不上&#xff0c;所以就弄一个excel生成sql的导入脚本&#xff0c;这样可以将需要的数据填到指定的列即可生成sql。 1、…

前端路由的实现原理

当谈到前端路由时&#xff0c;指的是在前端应用中管理页面导航和URL的机制。前端路由使得单页应用&#xff08;Single-Page Application&#xff0c;SPA&#xff09;能够在用户与应用交互时动态地加载不同的视图&#xff0c;而无需每次都重新加载整个页面。 在前端开发中&…

【VTKExamples::Meshes】第十八期 OBBDicer

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 公众号:VTK忠粉 前言 本文分享VTK样例OBBDicer,并解析接口vtkOBBDicer,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! 你的点赞就是我的动力(^U^)ノ~YO 1. …

AT7456E 贴片TSSOP-28 新版本 OSD字符叠加芯片

AT7456E OSD&#xff08;On-Screen Display&#xff09;叠加芯片的应用领域相当广泛&#xff0c;主要用于在视频信号上传递附加信息。根据您提供的信息[2]&#xff0c;以下是AT7456E的一些典型应用领域&#xff1a; 1.无人机&#xff1a;用于在无人机的视频传输中叠加关键信息…

NIKKE胜利女神妮姬1.5周年(PC)怎么下载一键下载安装教程一看就会

NIKKE胜利女神妮姬1.5周年(PC)怎么下载&#xff1f;一键下载安装教程一看就会 近日一款新型FPS游戏NIKKE引起了游戏爱好者们的热议&#xff0c;这款游戏是由Shift Up公司开发的一款二次元风格美少女射击类RPG游戏。玩家可以通过抽卡获取不同的角色&#xff0c;并通过主线支线关…

windows下git提交修改文件名大小写提交无效问题

windows系统不区分大小写&#xff0c;以及git提交忽略大小写&#xff0c;git仓库已存在文件A.js&#xff0c;本地修改a.js一般是没有提交记录的&#xff0c;需要手动copy一份出来A.js&#xff0c;再删除A.js文件提交仓库删除后&#xff0c;再提交修改后的a.js文件。 windows决…

Next.js 14 App Router引入 farmer-motion 初始化异常解决,顺带学点知识

前言 farmer-motion 是一个非常好用的动画库&#xff0c;当然用来做组件切换和路由切换过渡更不在话下。 记录一下&#xff0c;Next.js 14 App Router 下引入初始化异常的解决姿势&#xff0c;顺带扯一下 next.js 的知识点&#xff1b; 问题 过渡组件代码 我们拿 farmer-m…

SAP DMS修改文档操作简介

修改DMS文档的事物代码是—CV02N,同样的删除、审批都是用的CV02N对文档进行操作 1、文档的审批,根据我们后台对文档版本的配置通过CV02N对这个文档状态的一个变更 当审批后系统就会显示绿灯,如下图 2、文档的标记删除 我们在CV02N的界面中直接点击删除标记即可。 点击后…

Error opening file a bytes-like object is required,not ‘NoneType‘

错误显示&#xff0c;打开的是一个无效路径的文件 查看json文件内容&#xff0c;索引的路径与json文件保存的路径不同 方法&#xff1a;使用python脚本统一修改json文件路径 import json import os import argparse import cv2 from tqdm import tqdm import numpy as np impo…

算法设计优化——有序向量二分查找算法与Fibonacci查找算法

文章目录 0.概述1.语义定义2. 二分查找&#xff08;版本A&#xff09;2.1 原理2.2 实现2.3 复杂度2.4 查找长度 3.Fibonacci查找3.1 改进思路3.2 黄金分割3.3 实现3.4 复杂度分析3.5 平均查找长度 4. 二分查找&#xff08;版本B&#xff09;4.1 改进思路4.2 实现4.3 性能4.4 进…

基于CANoe从零创建以太网诊断工程(2)—— TCP/IP Stack 配置的三种选项

&#x1f345; 我是蚂蚁小兵&#xff0c;专注于车载诊断领域&#xff0c;尤其擅长于对CANoe工具的使用&#x1f345; 寻找组织 &#xff0c;答疑解惑&#xff0c;摸鱼聊天&#xff0c;博客源码&#xff0c;点击加入&#x1f449;【相亲相爱一家人】&#x1f345; 玩转CANoe&…

手撕netty源码(二)- 初始化ServerBootstrap

文章目录 前言一、ServerBootstrap 的创建和初始化1.1 创建1.2 初始化group1.3 初始化channel1.3 初始化option和attr1.4 初始化handler 和 childHandler 总结 前言 processOn文档跳转 接上一篇&#xff1a;手撕netty源码&#xff08;一&#xff09;- NioEventLoopGroup 本篇讲…