【Linux】动静态库:构建强大软件生态的基石

🌈 个人主页:Zfox_
🔥 系列专栏:Linux

目录

  • 一:🔥 动静态库基本原理
  • 二:🔥 认识动静态库
  • 三:🔥 动静态库的优缺点
    • 🦋 静态库
    • 🦋 动态库
  • 四:🔥 如何制作和使用动静态库?
    • 🦋 制作静态库
    • 🦋 使用静态库
      • 🥝 1.安装到系统里面
      • 🥝 2.手动调用我们自己的静态库
      • 🥝 3.使用带路径的库
    • 🦋 制作动态库
    • 🦋 使用动态库
      • 🥝 1.安装到系统里面
      • 🥝 2.手动调用我们自己的动态库
      • 🥝 3.使用带路径的库
  • 五:🔥 动态库报错
    • 🦋 解决方法
  • 六:🔥 动态库的加载
  • 七:🔥 共勉

一:🔥 动静态库基本原理

🦁 动静态库是可执行程序的“半成品”

所有的库本质就是可重定向二进制文件的集合,即目标文件的集合,其中包含了大量的执行方法

使用ldd 可执行程序命令查看可执行程序依赖的动静态库文件

二:🔥 认识动静态库

在 windows 中动态库以 .dll 为后缀,静态库以 .lib 为后缀。

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

  • 在使用gcc/g++生成可执行程序的时候,默认情况下生成的可执行程序就是动态链接动态库,如果想要静态链接静态库的话,需要使用 - static选项来静态编译生成可执行程序。

三:🔥 动静态库的优缺点

🦋 静态库

缺点

  • 浪费空间
  • 占磁盘空间,因为静态链接是将静态库中的代码加载到可执行程序中,所以静态编译的文件自己体积比较大。
  • 占内存空间,多个静态文件链接同一个静态库的时候,内存中会出现大量的重复代码

优点

  • 与库文件弱相关,不需要依赖库文件
  • 由于静态编译的时候,已经将静态库中的代码都放到可执行程序的正文代码中了,所以可执行程序本身是一个独立的文件

🦋 动态库

缺点

  • 与库文件强相关,必须依赖库文件
  • 如果将一个动态库文件从内存和磁盘中删掉,这个动态库链接的可执行程序就都不能运行了

优点

  • 节省空间
  • 节省内存空间,动态库文件在内存中只有一份,通过页表映射到不同进程的进程地址空间中的共享区共享。在静态链接的文件中,静态库的代码直接映射进了进程地址空间的正文代码中。

四:🔥 如何制作和使用动静态库?

🦁 我们在给被人静态库的时候,需要把 .a/.so 库文件和 .h 头文件给到对方。.a/.so 让别人可以调用库中的函数,.h 告诉别人库中有哪些函数可以被调用,相当于函数使用的说明书。

  • 在使用第三方库的时候,需要指明库的名称

🦁 假设有四个文件 my_stdio.c my_stdio.h my_string.c my_string.h,我们需要通过它们制作一个动静态库。

main.c

#include "my_stdio.h"
#include "my_string.h"
#include <stdio.h>int main()
{const char *s = "abcdefg";printf("%s: %d\n", s, my_strlen(s));return 0;
}

my_stdio.h

#pragma once#define SIZE 1024#define FLUSH_NONE 0
#define FLUSH_LINE 1
#define FLUSH_FULL 2struct IO_FILE
{int flag;     // 刷新方式int fileno;   // 文件描述符char outbuffer[SIZE];int cap;int size;// TODO
};typedef struct IO_FILE mFILE;mFILE *mfopen(const char *filename, const char *mode);
int mfwrite(const void *ptr, int num, mFILE *stream);
void mfflush(mFILE *stream);
void mclose(mFILE *stream);

my_stdio.c

#include "my_stdio.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>mFILE *mfopen(const char *filename, const char *mode)
{int fd = -1;if(strcmp(mode, "r") == 0){fd = open(filename, O_RDONLY);}else if(strcmp(mode, "w") == 0){fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0666);}else if(strcmp(mode, "a") == 0){fd = open(filename, O_CREAT | O_WRONLY | O_APPEND, 0666);}if(fd < 0) return NULL;mFILE *mf = (mFILE*)malloc(sizeof(mFILE));if(!mf){close(fd);return NULL;}mf->fileno = fd;mf->flag = FLUSH_LINE;mf->size = 0;mf->cap = SIZE;return mf;
}void mfflush(mFILE *stream)
{if(stream->size > 0){write(stream->fileno, stream->outbuffer, stream->size);fsync(stream->fileno);stream->size = 0;}
}int mfwrite(const void *ptr, int num, mFILE *stream)
{// 1.拷贝memcpy(stream->outbuffer + stream->size, ptr, num);stream->size += num;// 2.检测是否要刷新if(stream->flag == FLUSH_LINE && stream->size > 0 && stream->outbuffer[stream->size - 1] == '\n'){mfflush(stream);}return num;
}void mclose(mFILE *stream)
{if(stream->size > 0){mfflush(stream);stream->size = 0;}close(stream->fileno);
}

my_string.h

#pragma onceint my_strlen(const char *s);

my_string.c

#include "my_string.h"int my_strlen(const char *s)
{const char *end = s;while(*end != '\0') end++;return end - s;
}

Makefile

# 动态库
libmystdio.so:my_stdio.o my_string.ogcc -o $@ $^ -shared
%.o:%.cgcc -fPIC -c $<.PHONY:clean
clean:@rm -rf *.so *.o stdc*@echo "clean ... done".PHONY:output
output:@mkdir -p stdc/include@mkdir -p stdc/lib @cp -f *.h stdc/include@cp -f *.so stdc/lib @tar -czf stdc.tgz stdc @echo "output stdc ... done"# 静态库
libmystdio.a:my_stdio.o my_string.o@ar -rc $@ $^@echo "build $^ to $@ ... done"
%.o:%.c@gcc -c $<@echo "compling $< to $@ ... done".PHONY:clean
clean:@rm -rf *.a *.o stdc*@echo "clean ... done".PHONY:output
output:@mkdir -p stdc/include@mkdir -p stdc/lib @cp -f *.h stdc/include@cp -f *.a stdc/lib @tar -czf stdc.tgz stdc@echo "output stdc ... done"

🦋 制作静态库

  1. 编译同名的目标文件
gcc -c my_stdio.c
gcc -c my_string.c
  1. 生成静态库
  • 生成一个叫做 mystdio 的静态库需要使用 ar 命令,其中 -r 选项表示 replace,-c 表示 create
ar -rc libmystdio.a my_stdio.o my_string.o
  • mystdio 静态库前后需要加上lib和.a,使用的时候需省略

🦋 使用静态库

🥝 1.安装到系统里面

sudo cp *.h /usr/include/
sudo cp libmystdio.a /lib64/
  • 当我们将头文件和库文件安装到系统里的时候 我们只需使用 -l 命令告诉 gcc 我们使用的库是什么即可 注意去掉 lib 和 .a

🥝 2.手动调用我们自己的静态库

调用的函数的库文件在哪

  • 使用 -L + 库文件的路径(告诉编译器,在我指明的路径下找)

调用的库的名称

  • 使用 -l + 库的名称
gcc main.c -o main -L. -lmystdio

在这里插入图片描述

🥝 3.使用带路径的库

.PHONY:output
output:@mkdir -p stdc/include@mkdir -p stdc/lib @cp -f *.h stdc/include@cp -f *.a stdc/lib @tar -czf stdc.tgz stdc@echo "output stdc ... done"
  • 此时我们使用 makefile 将头文件和库文件分门别类放在 stdc 的目录下
root@hcss-ecs-a9ee:~/code/linux/112/lesson22/stdio# tree stdc
stdc
├── include
│   ├── my_stdio.h
│   └── my_string.h
└── lib└── libmystdio.a2 directories, 3 files

💦 调用的函数的头文件在哪

  • 使用 -I + 头文件的路径

💦 调用的函数的库文件在哪

  • 使用 -L + 库文件的路径

💦 调用的库的名称

  • 使用 -l + 库的名称
gcc main.c -Istdc/include -Lstdc/lib -lmystdio

在这里插入图片描述

🦋 制作动态库

  1. 生成目标文件

在生成同名的目标文件的时候,需要加上-fPIC选项,即产生与位置无关码,这样就可以让可执行程序找到对应的动态库。

gcc -fPIC -c my_stdio.c
gcc -fPIC -c my_string.c
  1. 生成动态库
    在打包动态库的时候,需要加上 -shared 选项形成库文件
gcc -o libmystdio.so my_stdio.o my_string.o  -shared

🦋 使用动态库

🥝 1.安装到系统里面

sudo cp *.h /usr/include/
sudo cp libmystdio.so /lib64/
  • 当我们将头文件和库文件安装到系统里的时候 我们只需使用 -l 命令告诉 gcc 我们使用的库是什么即可 注意去掉 lib 和 .a
gcc main.c -lmystdio

在这里插入图片描述

🥝 2.手动调用我们自己的动态库

当我们有了头文件和库文件的时候不安装到系统里

调用的函数的库文件在哪

  • 使用 -L + 库文件的路径(告诉编译器,在我指明的路径下找)
    调用的库的名称
  • 使用 -l + 库的名称
gcc main.c -o main -L. -lmystdio

在这里插入图片描述

🥝 3.使用带路径的库

.PHONY:output
output:@mkdir -p stdc/include@mkdir -p stdc/lib @cp -f *.h stdc/include@cp -f *.so stdc/lib @tar -czf stdc.tgz stdc@echo "output stdc ... done"
  • 此时我们使用 makefile 将头文件和库文件分门别类放在stdc的目录下
root@hcss-ecs-a9ee:~/code/linux/112/lesson22/stdio# tree stdc
stdc
├── include
│   ├── my_stdio.h
│   └── my_string.h
└── lib└── libmystdio.so2 directories, 3 files

💦 调用的函数的头文件在哪

  • 使用 -I + 头文件的路径

💦 调用的函数的库文件在哪

  • 使用 -L + 库文件的路径

💦 调用的库的名称

  • 使用 -l + 库的名称
gcc main.c -Istdc/include -Lstdc/lib -lmystdio

在这里插入图片描述

gcc main.c -lmystdio

五:🔥 动态库报错

🦁 这里也是需要特别注意:当运行可执行文件时,我们会发现找不到动态库 。这也就是动态库的特别之处,上述步骤跟静态库操作几乎一样,这里开始就发生转折。

./main: error while loading shared libraries: libmystdio.so: cannot open shared object file: No such file or directory

造成错误原因,在 gcc 下已形成可执行文件,程序编译已完成,执行文件的过程是和 gcc 无关,程序运行是操作系统管理,所以操作系统也需要找到动态库,总结就是:库文件没有在系统路径下,操作系统无法找到。

🦋 解决方法

Ubuntu 自身系统设定的相应的设置的原因,即其只在 /lib and /usr/lib 下搜索对应 的.so 文件,故需将对应 so 文件拷贝到对应路径。 centos则是 /lib/64

  1. 在 /lib/ 下建立软链接
sudo ln -s ~/code/linux/112/lesson22/stdio/libmystdio.so /lib/libmystdio.so
  1. 使用的时候需要导入.so动态库的路径 export LD_LIBRARY_PATH=
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:动态库的绝对路径

这里有需要注意:添加环境变量后,默认只在本次登录有效,下次登录时无效(默认清理登录前一次添加环境变量)。

  • 在 ~/ 下 有一个 .bashrc 隐藏文件,我们只需要将命令添加进来即可永久保存
  1. 直接拷贝动态库到系统目录下 /lib/64 /usr/lib64

  2. /etc/ld.so.conf.d/ 目录下建立自己的 xxx.conf 的配置文件,把动态库的路径放进去,然后 ldconfig

  • ldconfig 命令用于更新共享库的缓存。你需要运行此命令以使新的配置文件生效。

六:🔥 动态库的加载

静态库不需要加载

这个过程,在磁盘中 main.c 和 lib.c-库,会先在磁盘中形成一段代码,然后对这段代码进行编译,编译的本质就是预处理,编译-查找错误,形成二进制代码,然后进行汇编形成二级制指令。在编译阶段的时候就已经形成了虚拟地址空间。在虚拟地址空间中,这段代码也就被存入代码区,这个是根据不同区的特性所决定的。当执行这段代码的时候,操作系统就会直接在代码区进行访问。

在这里插入图片描述

动态库加载

🦁 动态库加载的过程,在磁盘中有一个 my.exe (可执行)和 lib.so(动态库),在形成可执行之前,编译阶段时,我们用到了 fPIC :产生位置无关码。

gcc -c -fPIC my_add.c

在这个阶段,动态库会将指定的函数地址,写入到可执行文件中。这个地址可以理解成 my_add.c (地址) + 偏移地址。

形成好可执行文件之后,磁盘将可执行文件拷贝到内存中,内存通过页表映射到虚拟地址空间的代码区中,当 OS 执行程序时,扫描到 my_add.c 是需要调用动态库的时候,程序会停下来,OS 会再通过函数的地址,然后页表映射去内存到磁盘中找动态库中,找到后拷贝到内存,又通过页表映射到共享区中。OS 再去共享区调用该方法,然后向下执行程序。
在这里插入图片描述

七:🔥 共勉

以上就是我对 【Linux】动静态库:构建强大软件生态的基石 的理解,觉得这篇博客对你有帮助的,可以点赞收藏关注支持一波~😉
在这里插入图片描述

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

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

相关文章

HRCE第四次实验

1.dns配置介绍 使用S/C架构 server --- bind(程序) --- 服务名称&#xff08;程序主进程&#xff09;named 开始实验前我们先进行主配置文件进行配置 vim /etc/named.conf进入主配置文件&#xff08;共分四部分&#xff09; 1.options{全局配置}&#xff08;多模块生效的配…

微服务设计模式 - 事件溯源模式(Event Sourcing Pattern)

微服务设计模式 - 事件溯源模式&#xff08;Event Sourcing Pattern&#xff09; 定义 事件溯源&#xff08;Event Sourcing&#xff09;是一种将所有状态更改保存为一系列事件的设计模式。每次系统状态发生变化时&#xff0c;都会生成一个事件&#xff0c;这些事件在事件存储…

Sketch下载安装,中文版在线免费用!

Sketch是一款轻便、高效的矢量设计工具&#xff0c;全球众多设计师借助它创造出了无数令人惊叹的作品。Sketch在下载安装方面&#xff0c;其矢量编辑、控件以及样式等功能颇具优势&#xff0c;不过&#xff0c;Sketch中文版即时设计在下载安装方面也毫不逊色。即时设计是一个一…

微服务之间的信息传递---OpenFeign拦截器

上篇我们已经实现了从网关传递信息到微服务。 新的问题是&#xff0c;微服务之间如何传递信息。 前面我们在公共模块中定义拦截器并保存用户信息到了线程变量。 但注意&#xff1a; 线程变量的作用域范围仅限于当前请求的线程。每个请求对应一个独立的线程变量&#xff0c;…

Nginx安装和配置

2.Nginx安装 2.1Nginx概述 2.1.1 Nginx介绍 Nginx&#xff08;engine x&#xff09;&#xff0c;2002 年开发&#xff0c;分为社区版和商业版&#xff08;nginx plus&#xff09; 2019 年3 月 15 日 F5 Networks 6.7 亿美元的价格收购 nginx是免费的、开源的、高性能的 HTTP…

2024全国铁路、高铁、地铁、有轨电车、窄轨铁路、单轨铁路、轻轨等矢量数据下载分享

数据是GIS的血液&#xff01; 我们在《全国地铁路线及站点SHP数据》一文中&#xff0c;分享了我国部分城市的地铁以及站点矢量数据。 现在又为你整理了2024全国铁路、高铁、地铁、有轨电车、窄轨铁路、单轨铁路、轻轨等矢量数据&#xff0c;你可以在文末查看该数据的下载领取…

git上传大文件的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

DAY16|二叉树Part04|LeetCode: 513.找树左下角的值、112. 路径总和、106. 从中序与后序遍历序列构造二叉树

目录 LeetCode: 513.找树左下角的值 基本思路 C代码 LeetCode: 112. 路径总和、113.路径总和II LeetCode: 112. 路径总和 C代码 LeetCode: 113.路径总和II LeetCode: 106. 从中序与后序遍历序列构造二叉树 基本思路 C代码 LeetCode: 513.找树左下角的值 力扣代码链接…

【算力基础】GPU算力计算和其他相关基础(TFLOPS/TOPS/FP32/INT8...)

文章目录 :one: 算力的常见指标:two: 算力计算:three: 常用链接 &#x1f680; 本文主要是聚焦于深度学习领域的 GPU的算力估计&#xff0c;其他类型的硬件设备如CPU可以类比参考。 1️⃣ 算力的常见指标 算力衡量主要与运算速度和精度这两个指标有关。 &#x1f314;速度指…

云集电商:如何通过 OceanBase 实现降本 87.5%|OceanBase案例

云集电商&#xff0c;一家聚焦于社交电商的电商公司&#xff0c;专注于‘精选’理念&#xff0c;致力于为会员提供超高性价比的全品类精选商品&#xff0c;以“批发价”让亿万消费者买到质量可靠的商品。面对近年来外部环境的变化&#xff0c;公司对成本控制提出了更高要求&…

认定出现不安全行为并通过系统发出告警信息的名厨亮灶开源了。

简介 AI视频监控平台, 是一款功能强大且简单易用的实时算法视频监控系统。愿景在最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;减少企业级应用约 95%的开发成本&#xff0c;在强大视频算法加…

yolov5快速复现

源码下载 进入github官网搜索yolov5(网址&#xff1a;https://github.com/ultralytics/yolov5)如下图红框所示&#xff1a; 进入界面如下图所示&#xff0c;点击右上角绿色Code&#xff0c;选择下载压缩包&#xff1a; 开发环境设置 本文使用云平台开发&#xff0c;系统为Li…

“再探构造函数”(2)

文章目录 一. 友元‘全局函数’作友元‘成员函数’作友元‘类‘作友元 二. 内部类三. 匿名对象四. 对象拷贝时的编译器优化分析调用时的顺序 一. 友元 何时会用到友元呢&#xff1f; 当想让&#xff08;类外面的某个函数/其它的类&#xff09;访问 某个类里面的(私有或保护的…

一周内从0到1开发一款 AR眼镜 相机应用?

目录 1. &#x1f4c2; 前言 2. &#x1f4a0; 任务拆分 2.1 产品需求拆分 2.2 开发工作拆分 3. &#x1f531; 开发实现 3.1 代码目录截图 3.2 app 模块 3.3 middleware 模块 3.4 portal 模块 4. ⚛️ 拍照与录像 4.1 前滑后滑统一处理 4.2 初始化 View 以及 Came…

Redis(2):内存模型

一、Redis内存统计 工欲善其事必先利其器&#xff0c;在说明Redis内存之前首先说明如何统计Redis使用内存的情况。 在客户端通过redis-cli连接服务器后&#xff08;后面如无特殊说明&#xff0c;客户端一律使用redis-cli&#xff09;&#xff0c;通过info命令可以查看内存使用情…

2024年CG作品网站推荐(精选篇)

ArtStation https://www.artstation.com GGAC https://www.ggac.com/

GraphRAG如何构建知识图谱Knowledge Graph

GraphRAG工作的第一步&#xff0c;是将输入的文档集合&#xff0c;按一定的策略拆分成一个一个chunks&#xff0c;然后解析每个chunks&#xff0c;将chunk中所关注的实体(entity)和关系(relation)解析出来&#xff0c;以此构建知识图谱。 那问题来了&#xff0c;GraphRAG是如何…

Spring Security 框架篇-深入了解 Spring Security 的认证功能流程和自定义实现登录接口(实现自定义认证过滤器、登出功能)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 Spring Security 框架概述 2.0 Spring Security 核心功能-认证功能 2.1 过滤器链 2.2 登录认证流程 2.3 思路分析 3.0 登录认证具体操作 3.1 环境搭建 3.2 实现 U…

JavaScript基础语法部分-黑马跟课笔记

一、Javascript介绍 1.JavaScript是什么&#xff1f; 1.是什么&#xff1f; 是一种运行在客户端&#xff08;浏览器&#xff09;的编程语言&#xff0c;实现人机交互效果 2.作用&#xff08;做什么&#xff1f;&#xff09; 网页特效&#xff08;监听用户的一些行为让网页做…

qt QDir详解

1、概述 QDir是Qt框架中的一个核心类&#xff0c;它提供了对文件系统目录的操作接口。Qt是一个跨平台的应用程序开发框架&#xff0c;广泛用于开发桌面、移动和嵌入式设备上的应用程序。QDir类使得开发者能够方便地在不同操作系统上处理目录和文件&#xff0c;如进行目录遍历、…