当前位置: 首页 > news >正文

【Linux】动静态库的制作

​🌠 作者:@阿亮joy.
🎆专栏:《学会Linux》
🎇 座右铭:每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力却得不到结果的日子,我们把它叫做扎根
在这里插入图片描述

目录

    • 👉动静库和静态库👈
    • 👉制作动静态库👈
      • 制作静态库
      • 制作动态库
    • 👉库的意义👈
    • 👉总结👈

👉动静库和静态库👈

  • 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。
  • 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
  • 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码。
  • 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)。
  • 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。而静态链接是将库的代码链接到可执行文件中,因此该可执行文件往往比较大。

👉制作动静态库👈

在工程项目开发中,我们往往会用到第三方提供的库。那第三方的库是如何制作出来的呢?第三方制作出来的库,是如何提供给我们使用的呢?为了回答两个问题,我们现在就来学习一下动静态库的制作。

制作静态库

为了更好地说明演示库的制作,我们先回顾一下以前的多文件的写法。头文件中放库中的头文件的包含和函数的声明等,源文件中放函数的实现等。

// myprint.h
#pragma once#include <stdio.h>
#include <time.h>extern void Print(const char* str);// mymath.h
#pragma once#include <stdio.h>extern int addToTarget(int from, int to);// myprint.c
#include "myprint.h"void Print(const char* str)
{printf("%s [%d]\n", str, (int)time(NULL));
}// mymath.c
#include "mymath.h"int addToTarget(int from, int to)
{int ret = 0;for(int i = from; i <= to; ++i){ret += i;}return ret;
}// main.c
#include "myprint.h"
#include "mymath.h"int main()
{Print("hello world");int ret = addToTarget(0, 100);printf("ret = %d\n", ret);return 0;
}

输入gcc main.c mymath.c myprint.c -o my.exe -std=c99指令就可以生成可执行程序 my.exe了。

在这里插入图片描述

假如现在我们是库的制作者,如果我们将头文件 .h 和目标文件 .o 给别人,别人是否可以实现我们所写好的库呢?其实是可以的,见下图所示:

在这里插入图片描述

在这里插入图片描述

将目标文件提供给别人去编译,是不方便别人使用的。因为目标文件多了,编译起来就会比较麻烦,而且传输的过程中还可能出现目标文件丢失的情况。所以我们需要将目标文件打包,而形成的包就是传说中的静态库了。

将目标文件打包成静态库指令

ar -rc libhello.a mymath.o myprint.o
#以下是注释
#ar是GNU中的归档工具 archive 归档
#-r replace 替换
#-c create 创建
#lib 库的前缀 .a 静态库的后缀 两者之间的内容就是静态库的名字

在这里插入图片描述

使用Makefile来生成静态库

libhello.a:mymath.o myprint.oar -rc libhello.a mymath.o myprint.o
mymath.o:mymath.cgcc -c mymath.c -o mymath.o -std=c99
myprint.o:myprint.cgcc -c myprint.c -o myprint.o -std=c99.PHONY:clean
clean:rm -rf *.o libhello.a

在这里插入图片描述
现在我们已经制作好了静态库,那么如果将这个静态库发布给别人使用呢?发布给别人使用的库中是包含 include 和 lib 两个文件夹,其中 include 中包含库的所有头文件,lib 包含的是对应的库文件。

那么,我们修改一下 Makefile,让它可以自动生成上方所述的路径。

libhello.a:mymath.o myprint.oar -rc libhello.a mymath.o myprint.o
mymath.o:mymath.cgcc -c mymath.c -o mymath.o -std=c99
myprint.o:myprint.cgcc -c myprint.c -o myprint.o -std=c99.PHONY:hello
hello:mkdir -p hello/includemkdir -p hello/libcp -rf *.h hello/includecp -rf *.a hello/lib.PHONY:clean
clean:rm -rf *.o libhello.a hello

在这里插入图片描述
将其他文件清理后,就剩下静态库和我们写的程序了。

在这里插入图片描述

接下来,我们就来学习一下如何使用静态库。

  • 将静态库拷贝到系统的默认搜索路径。头文件的默认搜索路径是/usr/include/,库文件的默认搜索路径是/lib64/或者/usr/lib64/

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

在这里插入图片描述
现在我们已经将静态库拷贝到了对应的路径下,那我们使用gcc main.c指令将代码编译一下,就会发现无法通过编译。原因是 gcc 默认链接的静态库是/lib64/libc.a,如果想让 gcc 链接我们写的静态库,就需要使用下方的指令了。

gcc main.c -lhello
#-l 链接 hello是静态库的名字 

在这里插入图片描述
注:将库拷贝到系统的默认路径下,就叫做库的安装。

将我们自己写的静态库拷贝到系统的默认链接下这种做法是不推荐的。因为我们自己写的库并没有经过可靠性验证,所以不太建议这种做法。那么我们需要将自己写的静态库移出系统的默认路径,该过程也称为库的卸载。

在这里插入图片描述

  • 指定头文件、库的路径和要链接的库
gcc main.c -I ./hello/include/ -L ./hello/lib/ -lhello
#-I 指明头文件所在的路径
#-L 指明库的路径
-l 指明所要路径的库(因为库路径下可能不止一个库)

在这里插入图片描述

制作动态库

静态库的代码是会被拷贝进可执行程序中;而动态库在没有被链接前会被编译好,当使用动态库时,并不是将代码拷贝到可执行程序中,而是让可执行程序与动态库产生关联。

生成目标文件

gcc -fPIC -c mymath.c -o mymath.o -std=c99
gcc -fPIC -c myprint.c -o myprint.o -std=c99
readelf -S 目标文件名 #查查看ELF格式的文件信息

-fPIC 选项的意思是形成与位置无关的目标二进制文件。动态库形成后,可以在内存的任意位置加载。而静态库的代码是拷贝到可执行程序中,可执行程序是有自己的地址空间的,所以静态库的代码需要拷贝到地址空间的特定位置,这就是与位置有关。静态库是按照绝对编址的方式,而动态库是按照相对编址(段地址+偏移量)的方式。

将目标文件打包

动态库的打包并不是使用 ar 指令,而是采用 gcc 加上 -shared 选项将目标文件打包成库。注意:一定要加上 -shared 选项,不然会被 gcc 识别成要生成可执行程序,从而报出没有 main 函数的错误。

gcc -shared myprint.o mymath.o -o libhello.so

在这里插入图片描述

以上就形成了动态库了,那么我们也来学习一下用 Makefile 来生成动态库。

.PHONY:all
all:libhello.so libhello.alibhello.so:mymath_d.o myprint_d.ogcc -shared mymath_d.o myprint_d.o -o libhello.so
mymath_d.o:mymath.cgcc -fPIC -c mymath.c -o mymath_d.o -std=c99
myprint_d.o:myprint.cgcc -fPIC -c myprint.c -o myprint_d.o -std=c99libhello.a:mymath.o myprint.oar -rc libhello.a mymath.o myprint.o
mymath.o:mymath.cgcc -c mymath.c -o mymath.o -std=c99
myprint.o:myprint.cgcc -c myprint.c -o myprint.o -std=c99.PHONY:output
output:mkdir -p output/includemkdir -p output/libcp -rf *.h output/includecp -rf *.a output/libcp -rf *.so output/lib.PHONY:clean
clean:rm -rf *.o *.a *.so output

在这里插入图片描述
将动静态库拷贝到 uselib 路径下

在这里插入图片描述
链接动态库

gcc main.c -I output/include/ -L output/lib/ -lhello

当存在同名的动静态库时,使用上方的指令形成的可执行程序是无法执行的,其报错原因是无法找到对应的动态库。那为什么会这样呢?

在这里插入图片描述

output/lib/路径下只有静态库时,却又可以生成对应的可执行程序,这又是为什么呢?

在这里插入图片描述
原因是 gcc 默认链接的是动态库,而如果只有我们自己制作的静态库,没有自己制作的动态库,那么就只能链接该静态库了。注意:系统的动态库还是动态链接的,只是静态链接自己制作的静态库。如果想让全部库都是静态链接的话,需要加上 -static 选项。-static 的意义就是摒弃优先使用动态库的原则,而是直接使用静态库。

在这里插入图片描述

如果存在同名的动静态库,默认使用的就是动态库,上面的报错只是找不到而已。那如何解决呢?解决这个问题,就需要了解动态库的加载了。

在这里插入图片描述

动态库的加载

如果是静态链接,静态库中的代码会被拷贝到可执行程序中。当可执行程序运行时,静态库的代码也被加载到内存了。而如果采用的是动态链接,可执行程序和动态库是分批加载的,并不是一起加载的。我们知道栈区和堆区之间存在一个共享区,而动态库的代码就是被加载到进程地址空间的共享区中去了。

在这里插入图片描述

当有多个进程使用同一个动态库的代码,这时候就不需要加载动态库的代码,只需要建立页表映射关系就行了。如果多个进程使用同一个静态库,那么内存中就会有多份静态库的代码,这样就会浪费内存空间了。这也就是动态库的意义。

当我们执行可执行程序时,不是已经指定了动态库所在的路径了吗?为什么还是找不到动态库呢?其实这是告诉 gcc 编译器,并不是告诉给操作系统的加载器。当程序运行加载时,就和 gcc 没有关系了,所以我们需要将动态库所在的路径告诉给操作系统的加载器,这样才能够找到动态库。

在这里插入图片描述
因为 C语言的动态可以就在系统的默认路径下,所以操作系统就能够找到对应的动态库;而静态库的代码是直接拷贝到可执行程序中的,不存在需要找的问题。那么,我们如果让操作系统找到自己制作的动态库呢?第一种方式就是将自己制作的动态库拷贝到系统的默认路径下,但这种方式不好。我们可以采用下方的方法:

  • 将动态库所在的路径导入到环境变量 LD_LIBRARY_PATH 中

系统可以通过环境变量 LD_LIBRARY_PATH 找到需要的动态库,将其加载到内存中。

在这里插入图片描述
这种方法有一个缺点,就是当我们将 Xshell 关掉时,再次启动时,会将导入到 LD_LIBRARY_PATH 中的路径都清空掉。

  • 新增配置文件

我们只需要在/etc/ld.so.conf.d/路径下创建一个.conf后缀的文件,该文件中保存动态库的路径,最后输入sudo ldconfig指令更新配置文件即可。

在这里插入图片描述
将配置文件删除后,还是找到我们的动态库的。原因是缓存中还有该配置文件,只要将配置文件更新一下就可以了。

  • /lib64/路径下建立一个与动态库的软链接

在这里插入图片描述

还有一种方法就是修改登录脚本,Linux 的登录脚本文件有两个,分别是 .bashrc.bash_profile,它们都是在家目录下的。.bash是被.bash_profile所调用的,那么我们可以在.bash_profile文件中增加动态库所在的路径即可,但这种方式不建议现在做。

在这里插入图片描述
在这里插入图片描述
以上就是动态库的制作了,现在我们来了解一下为什么要有库。

👉库的意义👈

  • 站在使用库的角度,库的存在,可以大大减少我们开发的周期,提高软件本身的质量(健壮性等)。
  • 站在写库的人的角度,1. 简单 2.代码安全。
  • 好玩的库:ncurses(基于字符的界面库),搜索关键词 Centos 7 yum 安装 ncurses。boost(C++ 的准标准库)。

👉总结👈

本篇博客主要讲解了什么是动静态库以及动静态库的制作等等。那么以上就是本篇博客的全部内容了,如果大家觉得有收获的话,可以点个三连支持一下!谢谢大家!💖💝❣️

相关文章:

【Linux】动静态库的制作

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《学会Linux》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;动静库和静…...

数据备份学习笔记2

Linux实现本地备份的命令&#xff1a; mkdir -p /root/backup/date "%Y-%m-%d" tar -zcvPf /root/backup/date "%Y-%m-%d"/test20230221.tar.gz /root/test20230221/ 我们再看下tar命令选项&#xff1a; tar -czvf txt3.tar.gz txt3 tar -xvf txt4.tar.g…...

webRTC

WebRTC是一种实时通信技术&#xff0c;可以在浏览器中实现音频、视频和数据的实时传输。WebRTC使用标准的API和协议&#xff0c;如RTCPeerConnection和RTCDataChannel等&#xff0c;可以实现点对点通信和多方会议等多种应用场景。WebRTC可以应用于Web前端、移动端和桌面端等多种…...

用Python搓一个黑洞

文章目录简介单位制观测绘图简介 黑洞图像大家都知道&#xff0c;毕竟前几年刚发布的时候曾火遍全网&#xff0c;甚至都做成表情包了。 问题在于&#xff0c;凭什么认为这就是黑洞的照片&#xff0c;而不是一个甜甜圈啥的给整模糊了得到的呢&#xff1f;有什么理论依据吗&…...

Spring MVC常用功能及注解

目录 一、什么是Spring MVC 1.1 Spring MVC定义 1.2 MVC定义 1.3 MVC和Spring MVC的关系 1.4 Spring MVC的作用 二、Spring MVC的使用 2.1 Spring MVC的创建和连接 2.1.1 RequestMapping注解 2.1.2 GetMapping注解 2.1.3 PostMapping注解 2.2 获取参数 2.2.1 获取单…...

shell 编程

文章目录一、shell 编程1.1. 脚本执行1.2. 变量1.3. 特殊变量1.4. 运算符1.5. for 循环1.6. while 循环1.7. case 语句1.8. read 命令1.9. if 判断1.10. 判断语句1.11. 自定义函数1.12. 脚本调试二、sed2.1. sed 选项2.2. sed function2.3. sed 删除&#xff08;d 命令&#xf…...

Leetcode.1401 圆和矩形是否有重叠

题目链接 Leetcode.1401 圆和矩形是否有重叠 Rating &#xff1a; 1709 题目描述 给你一个以 (radius, xCenter, yCenter)表示的圆和一个与坐标轴平行的矩形 (x1, y1, x2, y2)&#xff0c;其中 (x1, y1)是矩形左下角的坐标&#xff0c;而 (x2, y2)是右上角的坐标。 如果圆和矩…...

CHAPTER 3 Web Server - httpd配置(二)

Web Server - httpd配置二3.1 httpd配置3.1.1 基于用户的访问控制3.1.2 basic认证配置示例&#xff1a;1. 添加用户2. 添加网页文件3. 定义安全域4. 修改父目录权限5. 访问效果6. 在配置文件中定义一个".htaccess"隐藏文件7. 添加组3.1.3 虚拟主机1. 构建方案2. 基于…...

VSCode 连接 SSH 服务器

欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://blog.csdn.net/caroline_wendy/article/details/129133964 配置VSCode 下载VSCode:https://code.visualstudio.com/ 安装 Remote - SSH: 点击右下角蓝色图标: 连接服务器: 即可。 默认连接:ssh chen…...

如何选择靠谱的插画培训课程

如何选择靠谱的插画培训课程&#xff0c;今天教你3个维度选择一个靠谱的插画培训班&#xff01; 插画培训机构课程&#xff1a; 1.选择插画培训班时&#xff0c;要先考察课程&#xff0c;看看课程内容是否符合自己的需求&#xff0c;是否有助于提高插画技术。课程设置应该灵活…...

剑指 Offer 28. 对称的二叉树

剑指 Offer 28. 对称的二叉树 难度&#xff1a;easy\color{Green}{easy}easy 题目描述 请实现一个函数&#xff0c;用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样&#xff0c;那么它是对称的。 例如&#xff0c;二叉树 [1,2,2,3,4,4,3] 是对称的。 但是下…...

深入Spring底层透析后置处理器之豁然开朗篇

目录前言Spring的后置处理器Bean工厂后置处理器Bean后置处理器自定义Component实现注解开发前言 看这篇文章之前&#xff0c;需要了解Bean创建的过程&#xff0c;本篇文章是接着bean创建的基本流程的续写 Bean创建的基本过程&#xff1a;http://t.csdn.cn/1lK2d Spring的后置处…...

软件测试(基础定义篇)

测试基础 1、什么是软件测试&#xff1f;2、常见的测试分类3、质量模型 4、软件测试流程 5、测试用例 6、测试用例设计方法 )1、什么是软件测试&#xff1f; 1、什么是软件&#xff1f; 答&#xff1a;软件是控制计算机硬件工作的工具。 2、软件的组成&#xff1f; 3、什么是…...

华为OD机试 - 寻找目标字符串 | 机试题算法思路 【2023】

最近更新的博客 华为OD机试 - 简易压缩算法(Python) | 机试题算法思路 【2023】 华为OD机试题 - 获取最大软件版本号(JavaScript) 华为OD机试 - 猜字谜(Python) | 机试题+算法思路 【2023】 华为OD机试 - 删除指定目录(Python) | 机试题算法思路 【2023】 华为OD机试 …...

使用echart绘制中国地图并显示人数

文章目录引言效果如图所示vue中echarts4.9版本&#xff0c;地图的使用引言 在做毕设的过程中&#xff0c;有一个需求&#xff1a;根据用户的ip&#xff0c;在前端展示出中国地图&#xff0c;然后展现出每个省有多少人这样子 经过百度后&#xff0c;发现可以使用echart来完成该…...

Git的常用命令

1&#xff1a;软件安装1.1&#xff1a;Git下载与安装百度上搜索Git官网&#xff1a;https://git-scm.com/下载&#xff1a;https://git-scm.com/download/win下载Git安装程序&#xff0c;双击安装 Git-2.9.3.2-64-bit.exe配置环境变量path 使用git --version查看 git 是否安装成…...

AcWing1018.最低通行费

1018.最低通行费一个商人穿过一个 NN 的正方形的网格&#xff0c;去参加一个非常重要的商务活动。他要从网格的左上角进&#xff0c;右下角出。每穿越中间 1 个小方格&#xff0c;都要花费 1 个单位时间。商人必须在 (2N−1)(2−1) 个单位时间穿越出去。而在经过中间的每个小方…...

【面试题】vue中的插槽是什么?

大厂面试题分享 面试题库后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★地址&#xff1a;前端面试题库一、slot是什么在HTML中 slot 元素 &#xff0c;作为 Web Components 技术套件的一部分&#xff0c;是Web组件内的一个占位符该占位符可以在后期…...

Go语言结构体struct详解,Go空结构体的这些妙用你知道吗?

本文详解了Go语言结构体的各个知识点&#xff0c;最后介绍了空结构体的3种妙用。希望对你有帮助。 定义 结构体&#xff0c;是一种自定义的数据类型&#xff0c;由多个数据类型组合而成。用于描述一类事物相关属性。 定义方式&#xff1a; type 类型名 struct {字段名 字段类…...

华为OD机试 - 航天器(Python) | 机试题+算法思路+考点+代码解析 【2023】

航天器 题目 给航天器一侧加装长方形和正方形的太阳能板(图中的斜线区域); 需要先安装两个支柱(图中的黑色竖条); 再在支柱的中间部分固定太阳能板; 但航天器不同位置的支柱长度不同; 太阳能板的安装面积受限于最短一侧的那支支柱的长度; 现提供一组整型数组的支柱高度数据;…...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

《Playwright:微软的自动化测试工具详解》

Playwright 简介:声明内容来自网络&#xff0c;将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具&#xff0c;支持 Chrome、Firefox、Safari 等主流浏览器&#xff0c;提供多语言 API&#xff08;Python、JavaScript、Java、.NET&#xff09;。它的特点包括&a…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

【论文笔记】若干矿井粉尘检测算法概述

总的来说&#xff0c;传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度&#xff0c;通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档&#xff09;&#xff0c;如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下&#xff0c;风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...

如何更改默认 Crontab 编辑器 ?

在 Linux 领域中&#xff0c;crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用&#xff0c;用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益&#xff0c;允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...