【Linux】动静态库的制作

news2024/12/24 9:05:35

​🌠 作者:@阿亮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.o
	ar -rc libhello.a mymath.o myprint.o
mymath.o:mymath.c
	gcc -c mymath.c -o mymath.o -std=c99
myprint.o:myprint.c
	gcc -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.o
	ar -rc libhello.a mymath.o myprint.o
mymath.o:mymath.c
	gcc -c mymath.c -o mymath.o -std=c99
myprint.o:myprint.c
	gcc -c myprint.c -o myprint.o -std=c99

.PHONY:hello
hello:
	mkdir -p hello/include
	mkdir -p hello/lib
	cp -rf *.h hello/include
	cp -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.a

libhello.so:mymath_d.o myprint_d.o
	gcc -shared mymath_d.o myprint_d.o -o libhello.so
mymath_d.o:mymath.c
	gcc -fPIC -c mymath.c -o mymath_d.o -std=c99
myprint_d.o:myprint.c
	gcc -fPIC -c myprint.c -o myprint_d.o -std=c99

libhello.a:mymath.o myprint.o
	ar -rc libhello.a mymath.o myprint.o
mymath.o:mymath.c
	gcc -c mymath.c -o mymath.o -std=c99
myprint.o:myprint.c
	gcc -c myprint.c -o myprint.o -std=c99

.PHONY:output
output:
	mkdir -p output/include
	mkdir -p output/lib
	cp -rf *.h output/include
	cp -rf *.a output/lib
	cp -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++ 的准标准库)。

👉总结👈

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

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

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

相关文章

RS485通信总线详解

RS485 总线详解 RS-485 是美国电子工业协会&#xff08;EIA&#xff09;在 1983 年批准了一个新的平衡传输标准&#xff08;Balanced Transmission Standard&#xff09;也称作差分&#xff0c;EIA 刚开始将 RS&#xff08;Recommended Standard&#xff09;做为标准的前缀&am…

一文解决Rust字符串:String,str,String,str,CString,CStr

一、str和&str和String的区别 1.存放位置&#xff0c;可变不可变&#xff1a; str来源于Rust本身的数据类型&#xff0c;而String类型来自于标准库。首先看一下str 和 String之间的区别&#xff1a;String是一个可变的、堆上分配的UTF-8的字节缓冲区。而str是一个不可变的…

前端零基础入门-002-集成开发环境

本篇目标 了解市面上常用的前端集成开发环境&#xff08;ide&#xff09;掌握 HBuiberX 的使用&#xff1a;下载安装&#xff0c;新建项目、网页、运行网页。 内容摘要 本篇介绍了市面上流行的几款前端集成开发环境&#xff08;ide&#xff09;&#xff0c;并介绍了 Hbuilde…

华为OD机试 - 按索引范围翻转文章片段(Java) | 机试题算法思路 【2023】

使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。 华为 OD 清单查看地址:https://blog.csdn.net/hihell/category_12201821.html 华为OD详细说明:https://dream.blog.csdn.net/article/details/128980730 按索引范围翻转文章…

【黄啊码】什么是ElasticSearch?它会替代MySQL成为主流吗?如何优化?TP5如何接入ElasticSearch?

什么是ElasticSearch&#xff1f; Elasticsearch 是一个分布式、高扩展、高实时的搜索与数据分析 引擎。它能很方便的使大量数据具有搜索、分析和探索的能力。充分利用Elasticsearch的水平伸缩性&#xff0c;能使数据在生产环境变得更有价值。 Elasticsearch 的实现原理主要分…

「TCG 规范解读」第8章 TPM工作组 TPM 1.2中 SHA1的使用

可信计算组织&#xff08;Ttrusted Computing Group,TCG&#xff09;是一个非盈利的工业标准组织&#xff0c;它的宗旨是加强在相异计算机平台上的计算环境的安全性。TCG于2003年春成立&#xff0c;并采纳了由可信计算平台联盟&#xff08;the Trusted Computing Platform Alli…

Windows Server 2008 R2安装onlyoffice【docker】

目录 前言 准备工作 安装docker 安装onlyoffice 常见问题 前言 目前docker for windows只能在windows10/11上安装&#xff0c;其他的windows版本只能使用Docker Toolbox来安装&#xff0c;使用该工具安装的docker其实是借助了Oracle VM VirtualBox虚拟机来运行的&a…

简历的专业技能怎么写?排版需要注意的事项

一、简历的专业技能怎么写? 首先,先问一下你自己会什么,然后看看你意向的公司需要什么。一般HR可能并不太懂技术,所以他在筛选简历的时候可能就盯着你专业技能的关键词来看。对于公司有要求而你不会的技能,你可以花几 天时间学习一下,然后在简历上可以写上自己了解这个技…

使用sshdo限制incoming ssh只能执行指定命令

前言系统管理员经常面临一个问题&#xff0c;如何将用户的incoming ssh限制在一个命令白名单里&#xff0c;达到安全的目的。本文介绍sshdo工具&#xff0c;来实现该功能。仓库地址https://github.com/wanlinwang/sshdo安装tar xzf sshdo-1.1.tar.gz #如果是直接clone仓库则无需…

JVM学习笔记六:运行时数据区之堆

目录 概述 堆空间内部结构 JDK7版本 JDK8版本 堆空间的内存划分 堆空间大小设置参数 概述 Java堆是虚拟机所管理的内存中最大的一块&#xff0c;其在JVM启动时即被创建&#xff0c;并且空间大小也被确定&#xff08;这里是不考虑Java8之后以本地内存来实现的元空间&…

连续时间信号与离散时间信号

前言 《信号与系统》是一门很难的课&#xff0c;也是许多学校考研要考的专业课&#xff0c;由于每周只有两节课&#xff0c;所以每次上完都要及时的去复习&#xff0c;这里参考的教材是奥本海姆著作&#xff0c;刘海棠译&#xff0c;北京&#xff1a;电子工业出版社&#xff0…

实践数据湖iceberg 第四十课 iceberg的sql运维方式(合并文件、合并元数据、清理历史快照)

系列文章目录 实践数据湖iceberg 第一课 入门 实践数据湖iceberg 第二课 iceberg基于hadoop的底层数据格式 实践数据湖iceberg 第三课 在sqlclient中&#xff0c;以sql方式从kafka读数据到iceberg 实践数据湖iceberg 第四课 在sqlclient中&#xff0c;以sql方式从kafka读数据到…

只因小黑子:SVG

小黑子的SVG复习SFV画布1. 初始SVG2. SVG绘制矩形、圆形和椭圆形2.1 rect 矩形2.2 circle 圆形2.3 ellipse 椭圆4. SVG绘制线条、多边形和多线条4.1 line 线条4.2 polygon 多边形4.3 polyline 多线条5. SVG绘制文本 text6. SVG绘制路径 path7. SVG描边属性8. SVG 模糊和阴影效果…

MySQL安装配置教程(超级详细、保姆级)

一、 下载MySQL Mysql官网下载地址&#xff1a;https://downloads.mysql.com/archives/installer/ 选择想要安装的版本&#xff0c;点击Download下载 本篇文章选择的是5.7.31版本 二、 安装MySQL 选择设置类型 双击运行mysql-installer-community-5.7.31.0.msi&#xff0c;…

车载以太网 - 测试用例设计 - 时间参数 - 11

前面已经介绍过DoIP相关的时间参数信息,然而对于时间参数信息相关的测试用例该如何设计呢?个人认为这是用例中最好设计的一类,这类的用例只需要按照定义去设计写测试用例即可,难的是自动化脚本开发和手动测试执行。毕竟时间参数一般都是毫秒级的验证,就算是秒级的我们也很…

【项目管理】对管理的认识与思考

在进入职场一段时间后&#xff0c;我们不免会对管理有一些接触&#xff0c;可能会自己带团队、可能会到管理岗位等等&#xff1b;做管理重要的就是对于不同层级的管理。 管理是一门艺术&#xff0c;在谈到管理时&#xff0c;我们首先想到的应该是管人、带团队或者是一个部门或公…

matplotlib学习笔记(持续更新中…)

目录 1. 安装&#xff0c;导入 2. figure&#xff0c;axes&#xff08;图形&#xff0c;坐标图形&#xff09; 2.1 figure对象 2.2 axes对象 2.3 代码演示 2.3 subplot() 方法 3. 图表的导出 3.1 savefig() 方法 3.2 代码演示 1. 安装&#xff0c;导入 pip install m…

Python —— Windows10下配置Pytorch环境(完整流程)

最终效果 配置流程 一、下载安装显卡驱动 1、查看设备管理器显卡是否为NVIDIA&#xff0c;并确定显卡型号 2、根据显卡型号然后NVIDIA官网下载安装显卡驱动 下载完成后&#xff0c;双击一步一步执行即可。 3、安装完成驱动后&#xff0c;打开cmd终端输入"nvidia-smi"…

JVM学习笔记五:运行时数据区之本地方法栈

目录 概述 本地方法&#xff08;Native Method&#xff09; 使用本地方法的原因 本地方法栈 执行流程 概述 如果要了解本地方法栈的作用&#xff0c;首先需要了解本地方法库和本地方法接口。 本地方法接口是Java代码调用其他非Java代码的接口&#xff0c;本地方法库是其他…

观看课程领奖品!Imagination中国区技术总监全面解读 IMG DXT GPU

此前&#xff0c;我们发布了一系列关于 IMG DXT GPU 的介绍&#xff0c;为了让更多读者了解其背后的技术及应用方向&#xff0c;我们特别邀请 Imagination 中国区技术总监艾克录制全新在线课程&#xff0c;为大家全面解读IMG DXT GPU。 点击这里&#xff0c;马上注册观看&…