深入了解linux系统—— 库的制作和使用
什么是库?
库,简单来说就是现有的,成熟的代码;
就比如我们使用的C
语言标准库,我们经常使用输入scanf
和输出printf
,都是库里面给我们实现好的,我们可以直接进行服用。
库呢又分为静态库和动态库,在Linux
中静态库文件后缀.a
,动态库文件后缀.so
;在Windows
中静态库文件后缀.lib
,动态库文件后缀.dll
。
这里注意一下库的命名规则:
库的命名都是以
lib
开头,.a/.so
为后缀;去掉前缀lib
和后缀.a/.so
剩下的部分才是库的名字;例如
C
标准库libc.so
,去掉前缀和后缀,c
就是库的名字。
动静态链接
在之前我们知道gcc/g++
在编译时默认使用动态链接,若想要进行静态链接就要带-static
选项;
- 静态链接,本质上就是程序在编译链接时,将静态库的内容链接到可执行文件中,这样可执行程序在执行时就不会再依赖库;但是静态链接的可执行文件都比较大。
- 动态链接:本质上就是程序在编译链接时,在可执行文件和动态库之间建立某种关联,这样可执行程序在执行时机会依赖动态库。
静态库
库分为静态库和动态库,那什么是静态库呢?
静态库简单来说就是所有.o
文件的归档文件,也就是说静态库就是将所有的.o
文件合并在一起。
这样在链接形成可执行程序时,将静态库和.o
文件再合并在一起形成可执行文件。
静态库的制作
这里提供两份源文件代码mystdio.c
和mystring.c
来制作库
//mystdio.c
#include "mystdio.h"
MYFILE* BuyFile(int fd, int flag)
{MYFILE* myfile = (MYFILE*)malloc(sizeof(MYFILE));myfile->fileno = fd;myfile->flag = flag;myfile->bufflen = 0;myfile->flush_buff = LINE_FLUSH;//初始化缓冲区memset(myfile->outbuff, 0, sizeof(myfile->outbuff));return myfile;
}
MYFILE* MyOpen(const char* pathname, const char* mode)
{//确定文件的打开方式int fd = -1;int flag = 0;if(strcmp(mode, "w") == 0){flag = O_CREAT | O_WRONLY | O_TRUNC;fd = open(pathname, flag, 0666);}else if(strcmp(mode, "a") == 0){flag = O_CREAT | O_WRONLY | O_APPEND;fd = open(pathname, flag, 0666);}else if(strcmp(mode, "r")) {flag = O_RDONLY;fd = open(pathname, flag);}else{//???}if(fd < 0) return NULL;//打开文件失败return BuyFile(fd,flag);
}
void MyClose(MYFILE* file)
{if(file == NULL) return;if(file->fileno < 0) return;MyFlush(file);close(file->fileno);free(file);
}
int MyWrite(MYFILE* file, void* str, int len)
{//将数据拷贝到缓冲区当中//int n = 0;if(file->bufflen + len > MAX){//n = MAX - file->bufflen; //memcpy(file->outbuff + file->bufflen, str, n);//MyFlush(file);MyFlush(file);}//memcpy(file->outbuff + file->bufflen,(void*)((char*)str + n), strlen((char*)str) - n);memcpy(file->outbuff + file->bufflen,str,len);file->bufflen += len;if(file->flush_buff & NONE_FLUSH)MyFlush(file);else if((file->flush_buff & LINE_FLUSH) && (file->outbuff[file->bufflen-1] == '\n' || file->bufflen == MAX))MyFlush(file);else if((file->flush_buff & FULL_FLUSH) && (file->bufflen == MAX))MyFlush(file);return 0;
}
void MyFlush(MYFILE* file)
{if(file->bufflen <= 0) return;write(file->fileno, file->outbuff, file->bufflen);file->bufflen = 0;fsync(file->fileno);
}//mystring.c
#include "mystring.h"
int my_strlen(const char* str)
{ const char* s = str; while(*s != '\0') s++; return s - str;
}
头文件mystdio.h
和mystring.h
//mystdio.h
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define MAX 10//缓冲区大小
#define NONE_FLUSH 001 //0001
#define LINE_FLUSH 002 //0010
#define FULL_FLUSH 004 //0100
typedef struct IO_FILE{ int fileno;//文件描述符 int flag; char outbuff[MAX]; //缓冲区 int bufflen; //缓冲区内容长度 int flush_buff;
}MYFILE;
MYFILE* MyOpen(const char* pathname, const char* mode);
void MyClose(MYFILE* file);
int MyWrite(MYFILE* file, void* str, int len);
void MyFlush(MYFILE* file);
//mystring.h
int my_strlen(const char* str);
静态库是如何生成的呢?
静态库是.o
文件的归档文件,所以我们在制作库时,就要现将所有的.c
文件编译形成.o
文件
有了.o
文件,现在就要对这些.o
文件进行归档形成静态库;
这里就要使用指令ar -rc
(其中ar
是gnu
归档工具,-rc
表示replace
和create
)
静态库的使用
了解了静态库是如何制作的,那我们如何去使用静态库呢?
站在一个库的使用者的角度,我们拿到一个库时,我们并不知道这个库里都实现了哪些方法;我们就要参考所有的头文件。
所以我们就可以把头文件看做库的使用手册,在头文件中记录了库中实现的方法。
现在我们获得了静态库libmyc.a
和头文件mystdio.h
和mystring.h
我们通过查看头文件,知道了库libmyc.a
实现了哪些方法,实现了test.c
中使用了libmyc.a
中的方法
#include "mystdio.h" #include "mystring.h" int main() { MYFILE* myfile = MyOpen("log.txt","w"); if(myfile == NULL) return -1; const char* str = "abc-abc\n"; MyWrite(myfile, (void*)str, my_strlen(str)); MyWrite(myfile, (void*)str, my_strlen(str));MyClose(myfile); printf("%d\n",my_strlen(str)); return 0; }
这里我们直接编译test.c
:
可以看到,存在链接时报错,找不到这些方法;这是因为gcc
默认情况下只会去链接C
标准库,如果想要去链接第三方库,就要带-l
选项指明要链接的库。
gcc test.c -l库名
但是我们可以看到,-lmyc
指明了要链接哪一个库,却找不到这个库;
这是因为gcc
只会在指定路径下去寻找库,而我们要链接的库myc
在当前路径下,并不在系统的指定路径下;
所以我们要使用gcc
的-L
选项来指明我们要链接的库的路径
gcc test.c -l库名 -L库的路径
如上图所示,我们要链接第三方库,带-l
选项指明库的名称;如果要链接的库博主系统路径下,带-L
选项指明库的路径。
补充:gcc -I
选项
在上述操作中,我们的头文件都在当前路径下,如果头文件不在当前路径下呢?
我们把静态库和头文件分别放在./bin/lib
和./bin/include
路径下;
在编译时gcc
在当前路径下找不到头文件,就会报错;
解决方法:
- 在
gcc
编译时带-I
选项,指明头文件的路径。 - 在源文件引用头文件时,指明路径;
#include "./bin/include/mystdio.h"
动态库
动态库:程序在运行时才会去链接动态库的代码,多个程序可以共享;
一个可执行文件和动态库链接仅仅包含它用到的函数入口地址的一个表。
可执行文件在开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中拷贝到内存中;这一过程称为动态链接
动态库可以被多个程序共享,所以动态链接的可执行文件更下,节省了磁盘空间。
动态库的制作
我们知道了静态库是.o
的归档文件,那动态库呢?
动态库又是如何生成的呢?
还是上述的代码mystd.c
、mystring.c
、mystdio.h
和mystring.h
;
首先生成动态库,也是要先将所有的.c
文件编译形成.o
文件,与生成静态库不同的是,生成动态库在编译形成.o
文件是需要带-fPIC
选项,产生位置无关码。
其次,就是将这些.o
文件形成动态库,这里使用的是gcc
,-shared
选项
gcc -o libmyc.so *.o -shared
动态库的使用
动态库的使用和静态库使用,可以说一模一样的了;
这里就直接演示使用了:
这里我们发现一个问题,我们gcc
链接libmyc.so
库,编译链接形成了可执行程序a.out
,在运行时它找不到libmyc.so
库?
通过ldd
查看a.out
可执行程序依赖的库,可以发现确实找不到libmyc.so
库。
这是为什么呢?我们在gcc
编译时,使用-L
选项不是指明libmyc.so
的路径了吗?
这是因为我们
gcc
编译是-L
选项指明libmyc
的路径,这是告诉gcc
我们要链接的库在哪,但是系统并不知道我们的库在哪里;因为这里是动态链接,在可执行程序执行时,系统就会去找库
libmyc.so
,就会发现系统找不到这个库。
运行时搜索路径
那我们知道了动态链接我们自己的库,在可执行程序运行时,系统找不到我们的库,那如何解决这一问题呢?
这里解决方案有很多,我们一一来看:
首先,我们要知道,系统为什么找不到我们自己的库,却可以找到C
语言标准库?
因为我们C
语言标准库在系统的指定目录下,可执行程序在运行时,系统会在指定路径下去寻找,所以C
语言标准库就可以被系统找到。
1. 将我们的库拷贝到系统指定路径下,系统指定路径一般指/usr/lib
、/usr/local/lib
、/lib64
2. 在系统指定文件中建立软链接
这里我们库比较小,如果我们的库比较大,拷贝到系统指定路径下很不现实;
所以我们就可以在系统指定路径下建立同名软链接。
3. 更改环境变量LD_LIBRARY_PATH
上面两种方法,都是将我们的库放入(拷贝/软链接)系统指定文件中;
我们还可以通过修改环境变量LD_LIBRARY_PATH
,让我们的库能够被系统找到
这里,博主自己的系统配置过vim
,没有配置的该环境变量可能就是空了
我们可以修改这个环境变量,把我们库的路径加上去,这样系统就可以找到我们的库libmyc.so
了。
4. ldconfig
配置
除了上述三种方法之外呢,我们还可以进行配置/etc/ld.so.conf.d/
,并更新ldconfig
;
这样系统也可以找到我们的库libmyc.so
。
本篇文章的大致内容到这里就结束了,感谢各位大佬的支持
简单总结:
静态库制作:
ar -rc
将所有.o
位置归档。动态库制作:
gcc
的-shared
选项,将所有.o
文件形成动态库库的使用:
gcc
中-l
指定链接某些库,-L
指明要链接库所在的路径,-I
指明头文件所在的路径可执行程序在运行时,系统找到我们自己库的方法:将我们的库(拷贝/创建同名软链接)在系统指定路径中、修改环境变量
LD_LIBRARY_PATH
、配置/etc/ld.so.conf.d/
中的文件,并更新ldconfig
。
相关文章:

深入了解linux系统—— 库的制作和使用
什么是库? 库,简单来说就是现有的,成熟的代码; 就比如我们使用的C语言标准库,我们经常使用输入scanf和输出printf,都是库里面给我们实现好的,我们可以直接进行服用。 库呢又分为静态库和动态…...
Java中的设计模式:单例模式的深入探讨
单例模式的原理 单例模式的核心在于控制实例的数量。在Java中,类的实例化通常是由new关键字完成的。然而,单例模式通过将构造器私有化(private),阻止了外部通过new关键字直接创建类的实例。取而代之的是,单…...
View的工作流程——measure
1.DecorView被加载到Window当中 调用Activity的startActivity方法的时候,最终调用的是ActivityThread的handleLaunchActivity方法来创建Activity。 onResume方法也是在ActivityThread中的handleResumeActivity方法中被调用的,我们之前提到的DecorView就…...
【系统架构设计师】2025年上半年真题论文回忆版: 论软件测试方法及应用(包括解题思路和参考素材)
更多内容请见: 备考系统架构设计师-专栏介绍和目录 文章目录 真题题目(2025年上半年 试题4)解题思路1. 核心问题2. 论文目标3. 突出系统架构与 AI 的协同价值论文素材参考1、AI工具在生成测试用例过程中的具体应用2、AI测试用例生成的基本处理流程真题题目(2025年上半年 试…...

《软件工程》第 13 章 - 软件维护
知识思维导图 13.1 软件维护与进化的概念 1. 核心概念 软件维护:软件交付使用后,为纠正错误、改善性能或其他属性而进行的修改过程软件进化:随着时间推移,软件系统为适应环境变化和用户需求而不断演变的过程 2. 维护类型&#…...

2024 CKA模拟系统制作 | Step-By-Step | 12、创建多容器Pod
目录 免费获取题库配套 CKA_v1.31_模拟系统 一、题目 二、考点分析 1. 多容器 Pod 的理解 2. YAML 配置规范 3. 镜像版本控制 三、考点详细讲解 1. 多容器 Pod 的工作原理 2. 容器端口冲突处理 3. 资源隔离机制 四、实验环境搭建步骤 总结 免费获取题库配套 CKA_v…...

python:selenium爬取网站信息
关注我,精彩不错过! 前言 使用python的requests模块还是存在很大的局限性,例如:只发一次请求;针对ajax动态加载的网页则无法获取数据等等问题。特此,本章节将通过selenium模拟浏览器来完成更高级的爬虫抓…...
Nginx版本平滑迁移方案
Nginx版本平滑迁移方案 最可靠方案:make install后,先-s stop再重启,100%确保版本切换特殊情况:当发现nginx.pid.oldbin文件时,才考虑使用USR2信号无损升级避坑重点:make install只是替换文件,…...
WPF 按钮悬停动画效果实现
WPF 按钮悬停动画效果实现 下面我将实现一个专业的按钮悬停动画效果:当鼠标悬停在按钮上时,按钮上的文字由黑色变为白色,同时加粗并变大。 完整实现方案 MainWindow.xaml <Window x:Class"ButtonHoverEffect.MainWindow"xml…...

满天星之canvas实现【canvas】
展示 文章目录 展示Canvas 介绍【基础】简介兼容性关键特性注意事项应用场景:基本示例 满天星代码实现【重点】代码解释 全量代码【来吧,尽情复制吧少年】html引入JS代码 参考资源 Canvas 介绍【基础】 简介 Canvas是一个基于HTML5的绘图技术࿰…...
我在架构师面前谈 Spring Inner Beans,他直接点头说:这人有料!
“你听说了吗?阿里、字节最近的Java面试题又加难了!” “嗯?咋了?” “Spring又被拿出来问了,这次居然问到了Inner Beans!” “这不是冷门题吗?” “是啊,我一开始还真没答上来……” 是的!今天要跟大家唠嗑的,就是这个在面试中悄悄冒头,但平时开发中却经常被我们忽…...
Java无序数组 vs 有序数组:性能对比与选型指南
在Java中选择使用无序数组还是有序数组,需根据具体的应用需求和操作特性进行权衡。以下是从不同维度分析的详细对比及建议: 一、核心操作的性能对比 操作无序数组有序数组插入/追加O(1)(直接尾部插入)O(n)(需移动元素…...
【Linux 基础知识系列】第二篇-Linux 发行版概述
一、什么是 Linux 发行版? Linux 发行版是指将 Linux 内核和应用程序、工具、库等有机组合在一起,形成一个完整的操作系统。由于 Linux 的开源特性,任何人都可以在 Linux 内核的基础上进行修改和定制,因此产生了许多不同的发行版…...

【开源解析】基于PyQt5+Folium的谷歌地图应用开发:从入门到实战
🌐【开源解析】基于PyQt5Folium的谷歌地图应用开发:从入门到实战 🌈 个人主页:创客白泽 - CSDN博客 🔥 系列专栏:🐍《Python开源项目实战》 💡 热爱不止于代码,热情源自每…...

在 Ubuntu 22.04 LTS 上离线安装 Docker
在 Ubuntu 22.04 LTS 上离线安装 Docker 一、准备工作 1.1 获取目标系统信息 在目标 Ubuntu 22.04 LTS 系统上,先执行以下命令确认架构信息: uname -m lsb_release -a一般返回如下信息: 1.2 需要一台可联网的机器 准备一台可以连接互联网…...

python调用langchain实现RAG
一、安装langchain 安装依赖 python -m venv env.\env\Scripts\activatepip3 install langchainpip3 install langchain-corepip3 install langchain-openaipip3 install langchain-communitypip3 install dashscopepip3 install langchain_postgrespip3 install "psyc…...
Qt 中的 d-pointer 与 p-pointer小结
Qt 中的 d-pointer 与 p-pointer: PIMPL 惯用法解析 在 Qt 库中,尤其是在其核心类和模块中,广泛使用了 PIMPL (Pointer to IMPLementation,指向实现的指针) 的编程惯用法。这种模式通过一对指针来实现:d-pointer (d_p…...
冷库耗电高的一种重要原因分析,以及一种降低冷库电费≥20%的方法
1.冷库耗电高的原因分析 1.1化霜不及时 固定周期化霜:传统定时化霜模式(如每日2次)未考虑实际结霜量,导致无效化霜(霜层薄时仍启动化霜),浪费大量电能。 化霜时间过长:化霜加热器…...
理解 Redis 事务-21(使用事务实现原子操)
使用事务实现原子操作 Redis 事务是一种在单个步骤中执行一组命令的机制。"要么全部,要么全部不"的方法确保了数据的一致性和完整性,尤其是在需要对相关数据进行多个操作时。没有事务,并发操作可能会导致竞争条件和不一致的数据状…...
神经网络加上注意力机制,精度反而下降,为什么会这样呢?注意力机制的本质是什么?如何正确使用注意力机制?注意力机制 | 深度学习
在深度学习的发展中,注意力机制的引入曾被誉为一次划时代的技术飞跃。无论是在自然语言处理领域产生Transformer架构,还是在图像识别、语音识别和推荐系统等多个方向取得显著成效,注意力机制的价值似乎毋庸置疑。然而,在一些实际应用场景中,研究人员和工程师却发现:在传统…...

触控精灵 ADB运行模式填写电脑端IP教程
•ADB模式,如果你手机已经root则可以直接运行,无需安装电脑端。 •ADB模式,如果你手机没有root,那你可以windows电脑下载【极限投屏】软件,然后你的手机和电脑的网络要同一个wifi,然后把你电脑的ip地址填写…...

uniapp|实现多端图片上传、拍照上传自定义插入水印内容及拖拽自定义水印位置,实现水印相机、图片下载保存等功能
本文以基础视角,详细讲解如何在uni-app中实现图片上传→水印动态编辑→图片下载的全流程功能。 目录 引言应用场景分析(社交媒体、内容保护、企业素材管理等)uniapp跨平台开发优势核心功能实现图片上传模块多来源支持:相册选择(`uni.chooseImage`)与拍照(`sourceType:…...

linux有效裁剪视频的方式(基于ffmpeg,不改变分辨率,帧率,视频质量,不需要三方软件)
就是在Linux上使用OBS Studio录制一个讲座或者其他视频,可能总有些时候会多录制一段时间,但是如果使用剪映或者PR这样的工具在导出的时候总需要烦恼导出的格式和参数,比如剪映就不支持mkv格式的导出,导出成mp4格式的视频就会变得很…...

服务器密码安全运维解决新思路:凭据管理SMS+双因素SLA认证结合的方案
引言:云服务器安全成本困局 在云计算渗透率突破60%的今天,中小企业正面临严峻的安全悖论:某权威机构数据显示,72%的云上数据泄露事件源于凭据管理不当,而传统安全解决方案的采购成本往往超过中小企业年利润的8%。这种…...

论文阅读笔记——In-Context Edit
ICEdit 论文阅读笔记 指令图像编辑现有方法的局限: 微调类方法(InstructPix2Pix、Emu Edit、 Ultra Edit):需要大规模数据和算力、精度高但效率低且泛化性低;免训练方法(Prompt-to-Prompt、 StableFlow&am…...
Debian 系统 Python 开发全解析:从环境搭建到项目实战
Debian 系统 Python 开发全解析:从环境搭建到项目实战 在当今数字化时代,Python 凭借其简洁易读的语法和强大的功能,成为了最受欢迎的编程语言之一。Debian 作为一款稳定、安全且开源的 Linux 操作系统,为 Python 开发提供了理想的环境。本文将详细介绍在 Debian 系统上进…...
Next.js 15 与 Apollo Client 的现代集成及性能优化
Next.js 15 与 Apollo Client 的现代集成及性能优化 目录 技术演进集成实践性能优化应用案例未来趋势 技术演进 Next.js 15 核心特性对开发模式的革新 Next.js 15 通过引入 App Router、服务器组件(Server Components)和客户端组件(Clie…...

【后端高阶面经:MongoDB篇】41、MongoDB 是怎么做到高可用的?
一、MongoDB高可用核心架构:副本集(Replica Set)设计 (一)副本集角色与拓扑结构 1. 三大核心角色 角色职责描述资源占用选举权重数据存储Primary唯一接收写请求的节点,将操作日志(Oplog&…...
IO Vs NIO
一、IO(传统阻塞式) 全称:Input/Output(输入/输出) 定义:Java 1.0 引入的基础 I/O 模型,基于流(Stream)的同步阻塞操作,线程在读写数据时会阻塞直到操作完成。 二、NIO(新式非阻塞式) 全…...
offset 家族和 client 家族
在前端开发中,offset 家族和 client 家族是用于获取元素尺寸和位置的重要属性集合。以下是对这两个家族相关知识点的系统总结: 一、offset 家族 核心属性 offsetWidth / offsetHeight 含义:元素的总尺寸,包含内容区、内边距、边…...