【Linux基础IO】Linux IO编程入门:揭秘动态库与静态库的秘密
📝个人主页🌹:Eternity._
⏩收录专栏⏪:Linux “ 登神长阶 ”
🤡往期回顾🤡:Linux Shell
🌹🌹期待您的关注 🌹🌹
❀Linux基础IO
- 📒1. 库的作用
- 📚2. 静态库
- 🌞生成静态库
- ⭐使用动态库
- 📜3. 动态库
- 🌞生成动态库
- 🌸使用动态库
- 📝4. 动态库加载
- 📖5. 总结
前言:在Linux的浩瀚编程海洋中,IO(输入/输出)操作作为系统与外界交互的基石,其重要性不言而喻。无论是文件读写、网络通信还是设备驱动,都离不开IO操作的支撑。而在这个过程中,动静态库作为代码复用和模块化的重要手段,扮演着至关重要的角色。它们不仅简化了开发流程,提高了开发效率,还通过优化资源使用和减少编译时间等方式,为Linux程序的性能和可维护性保驾护航
然而,对于许多Linux编程初学者和进阶者而言,动静态库的选择、配置与使用仍然是一个相对陌生且充满挑战的领域。面对琳琅满目的库文件和复杂的链接过程,如何快速上手并有效利用这些资源,成为了摆在他们面前的一道难题
本文旨在成为您学习Linux基础IO中动静态库的得力助手。我们将从最基本的概念出发,逐步深入解析动静态库的原理、区别及适用场景。通过生动的实例和详细的步骤,我们将带您一起探索如何在Linux IO编程中巧妙地运用动静态库
让我们一同踏上这段充满挑战与收获的学习之旅,共同探索Linux基础IO中动静态库的无限可能!
📒1. 库的作用
代码复用:
库提供了可重用的代码集合,这些代码可以在多个程序中共享。通过使用库,开发者可以避免重复编写相同的代码,从而减少开发时间,提高开发效率。代码复用还可以减少因重复编码而导致的错误和漏洞
模块化编程:
库使得模块化编程成为可能。模块化编程是一种将程序划分为独立、可重用的模块或组件的方法。每个模块都封装了特定的功能或数据,并提供了与其他模块交互的接口。通过使用库,开发者可以将复杂的程序分解为更小、更易于管理的部分,从而更容易地进行开发和维护
使用库能极大的简化代码,且具有很多优势,这些优势使得开发者能够更高效地开发软件,提高软件的质量和可维护性
库一般分为:
- 静态库
- 动态库
下面我将用两种方式,模拟一个能够加减乘除的库,来方便大家理解
头文件.h
// 为了简化代码,将他们放在一起展示
// Add.h
#pragma once
#include <stdio.h>
extern int Add(int x, int y);
// Sub.h
#pragma once
#include <stdio.h>
extern int Sub(int x, int y);
// Mul.h
#pragma once
#include <stdio.h>
extern int Mul(int x, int y);
// Div.h
#pragma once
#include <stdio.h>
extern int Div(int x, int y);
然后我们再编写一个.c文件,将这些声明定义一下,这里就不展示了
📚2. 静态库
静态库(Static Library)是程序设计中用于存储可重用代码和数据的一种文件类型。它们通常是在编译时与程序的其他部分一起被链接到最终的可执行文件中,因此被称为“静态”的,因为它们在程序执行期间不会改变
当我们在上面代码的基础上,如果我们想编译,执行一个文件,在使用上是有点复杂的,我们需要先将它们编译成二进制.o文件,在进行链接,所以我们可以将它们打包成一个文件,然后快速实现我们想要的程序
指令:
ldd 可执行文件
可以查看可执行文件的链接状态
🌞生成静态库
指令:
ar -rc 静态库 链接的文件
Makefile:
static-lib=libmymath.a
$(static-lib):Add.o Sub.o Mul.o Div.o
ar -rc $@ $^
%.o:%.c
gcc -c $<
.PHONY:output
output:
mkdir -p mymath_lib/include
mkdir -p mymath_lib/lib
cp -f *.h mymath_lib/include
cp -f *.a mymath_lib/lib
.PHONY:clean
clean:
rm -rf *.o *.a mymath_lib
这样我们就简单生成了一个静态库,那么我们该如何去使用这个静态库呢?,其实只要在编译时,链接库就可以了
⭐使用动态库
在编译时,需要指定静态库文件的位置,以便编译器能够找到并链接它。链接器(Linker)会将静态库中的代码和数据复制到最终的可执行文件中
指令:
gcc TestMain.c -I 新增头文件搜索路径 -l链接的库名称 -L 新增库文件搜索路径
-I
指定头文件路径
-L
指定库路径
-l
指定库名
测试目标文件生成后,静态库删掉,程序照样可以运行
注意:-l
加上链接的库名称是去掉lib和文件后缀的 (比如libmymath.a ->-lmymath
)
最后我们可以直接运行这个可执行程序
注意事项:
- 静态库通用于提供基础功能或算法,这些功能或算法在多个程序中都会用到,且更新不频繁
- 随着模块化编程和动态链接技术的发展,静态库的使用逐渐减少,特别是在需要减小程序体积和共享代码库更新的场景下。
- 在某些情况下,如嵌入式系统或需要高度安全性的环境中,静态库仍然是首选方案
📜3. 动态库
动态库,又称动态链接库(Dynamic Link Library,简称DLL),在Unix-like系统上称为Shared Object(简称SO),是一种包含可由多个程序同时使用的代码和数据的库文件,动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间
🌞生成动态库
dy-lib=libmymath.so
$(dy-lib):Add.o Sub.o Div.o Mul.o TestMain.o
gcc -shared -o $@ $^
%.o:%.c
gcc -fPIC -c $<
.PHONY:output
output:
mkdir -p mymath_lib/include
mkdir -p mymath_lib/lib
cp -f *.h mymath_lib/include
cp -f *.so mymath_lib/lib
.PHONY:clean
clean:
rm -rf *.o *.so mymath_lib
shared
:表示生成共享库格式
fPIC
:产生位置无关码(position independent code)
库名规则:libxxx.so
🌸使用动态库
当我们在我们的库文件目录中,同时存在静态库和动态库时,链接器(如gcc的ld)回默认优先使用动态库,如果我们不进行一下操作,虽然可以生成可执行文件,但是文件并不能实现我们的功能,因为它没有正确链接
方法一:直接安装到系统中
直接安装到系统当中,是最推荐的一种方式,也是用的最多的方式,下面我们来演示一下
我们系统文件中会有存放头文件和库文件的地方,我们只需要将我们我们头文件和库文件,拷贝到对应目录下,我们就可以直接使用动态库链接
/usr/include // 头文件
/lib64 // 库文件
我们将我们自己的文件,安装到了系统文件中,下面我们就可以使用动态库了
方法二:通过使用软连接,查找动态库
在Linux或Unix系统中,动态库(Dynamic Libraries)通常以.so(Shared Object)文件的形式存在,这些库在程序运行时被加载到内存中,允许程序共享和复用代码。使用软连接(也称为符号链接或symlink)来管理动态库可以方便地更新或替换库文件,而不需要修改依赖于这些库的应用程序
当然我们也可以直接将软连接安装到系统文件中
方法三:使用环境变量的方式
在Linux或Unix系统中,环境变量经常被用来指定系统或应用程序查找动态库(.so 文件)的路径。特别是
LD_LIBRARY_PATH
环境变量,它允许用户为动态链接器(dynamic linker/loader)指定额外的库搜索路径
方法四:更改系统关于动态库的配置文件
在Linux系统中,更改系统关于动态库的配置文件以查找动态库,主要涉及编辑
/etc/ld.so.conf
文件或/etc/ld.so.conf.d/
目录下的配置文件
先在目录下创建一个文件
在文件中,保存一下我们库的路径
运行ldconfig,让更改生效
使用外部库
系统中其实有很多库,它们通常由一组互相关联的用来完成某项常见工作的函数构成。比如用来处理屏幕显示情况的函数(ncurses库)
使用外部库的本质就是将外部库的头文件和库文件安装到我们的系统目录下,我们就可以使用这个库了
📝4. 动态库加载
程序没有被加载,程序内部也是有地址的, 变量名,函数名等,编译成为2进制,编译的时候,对代码进行编址,基本遵守虚拟地址空间的那一套虚拟地址空间,不仅仅是OS里面的概念。编译器编译的时候,也要按照这样的规则编译可执行程序,这样才能在加载的时候,进行从磁盘文件到内存,在进行映射,也就是具有了“虚拟地址”,也就是逻辑地址
逻辑地址:基地址 + 偏移量,当基地址从0开始,[0, FFFFFFFF]的时候,这是被称作平坦模式
绝对编址: 简而言之,就是将程序中的每个部分(代码、数据等)按照它们在文件中的顺序,从上到下、从前到后进行连续的编址。这种方式下,程序中的每个元素都有一个唯一的、固定的内存地址,适用于平坦模式
相对编址: 按照与一个参照物的的距离来编址,这种编址方式使得程序在内存中的具体位置具有一定的灵活性,只要保持元素之间的相对位置不变,程序就能正确执行
📖5. 总结
在探索Linux基础IO(输入输出)的旅程中,我们深入了解了动态库(Dynamic Libraries)与静态库(Static Libraries)这一对重要概念,它们不仅是Linux系统编程的基石,也是理解现代软件开发中模块化、复用与性能优化的关键一环
然而,正如硬币的两面,动态库也带来了诸如版本兼容性问题、依赖关系复杂等挑战。因此,在实际开发中,选择使用静态库还是动态库,需要根据项目的具体需求、目标平台的特性以及团队的技术栈等多方面因素综合考虑
总之,Linux基础IO中的动静态库不仅是技术层面的选择,更是对软件开发理念、效率与可维护性之间平衡的深刻体现。随着技术的不断进步,我们期待看到更多创新性的解决方案,能够进一步简化开发流程,提升软件质量,让开发者能够更加专注于业务逻辑的实现,而非被底层技术细节所束缚,愿我们都能在技术的海洋中乘风破浪,不断前行,共同推动软件技术的发展与进步
希望本文能够为你提供有益的参考和启示,让我们一起在编程的道路上不断前行!
谢谢大家支持本篇到这里就结束了,祝大家天天开心!