【Linux篇】革新编程方式:如何开发让人眼前一亮的库
库的秘密:颠覆传统思维的编程组件打造术
- 一. 库的概念
- 二. 静态库
- 2.1 创建静态库的命令
- 2.2 使用静态库
- 三. 动态库(重点)
- 3.1 创建动态库的命令
- 3.2 动态库问题
- 3.2.1 解决方案
- 四. 最后
在现代软件开发中,库(Library)扮演着重要角色。通过对库的结构、功能和实现原理的深入剖析,我们可以更高效地复用代码、提高开发效率。接下来,我们将结合实例,逐步讲解库的核心概念、设计思路及制作流程,为大家搭建系统化的知识框架,助力自主构建高质量的代码库。
💬 欢迎讨论:如果你在学习过程中有任何问题或想法,欢迎在评论区留言,我们一起交流学习。你的支持是我继续创作的动力!
👍点赞、收藏与分享:觉得这篇文章对你有帮助吗?别忘了点赞、收藏并分享给更多的小伙伴哦!你们的支持是我不断进步的动力!
🚀分享给更多人:如果你觉得这篇文章对你有帮助,欢迎分享给更多对Linux OS感兴趣的朋友,让我们一起进步!
一. 库的概念
库(Library)是指一组已封装、可复用的代码集合,通常以函数、类或模块的形式提供。它将常见或复杂的功能抽象出来,供开发者直接调用,而无需重复实现。根据链接方式,可分为静态库(在编译时合并到可执行文件)和动态库(在运行时加载);根据用途,又可细分为通用工具库、领域专用库等。通过引入库,项目能够保持结构清晰、降低耦合、加快开发速度,同时也便于维护和升级。
引言:本人已经模拟实现mylibc库,跟原始的libc库肯定比不了,只是为了理解库过程即可。代码如下:
// my_stdio.h
#pragma once
#define SIZE 1024
#define FLUSH_NONE 0
#define FLUSH_LINE 1
#define FLUSH_FULL 2
struct 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 mfclose(mFILE *stream);
// my_stdio.c
#include "my_stdio.h"
#include
#include
#include
#include
#include
#include
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_WRONLY|O_TRUNC, 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]== '
')
{
mfflush(stream);
}
return num;
}
void mfclose(mFILE *stream)
{
if(stream->size > 0)
{
mfflush(stream);
}
close(stream->fileno);
}
// my_string.h
#pragma once
int my_strlen(const char *s);
// my_string.c
#include "my_string.h"
int my_strlen(const char *s)
{
const char *end = s;
while(*end != ' ')end++;
return end - s;
}
简单模拟实现打开、写、关闭文件,将语言层缓冲区的数据刷新到内核缓冲区,求字符串长度等。
二. 静态库
静态库一般以.a或.lib为后缀,一般是静态库。
静态库(Static Library)是编译好的目标文件(.o 或 .obj 文件)的集合,它在编译时直接链接到应用程序中。静态库的内容会在编译过程中与程序的目标文件合并,生成最终的可执行文件。由于静态库在编译时已经链接到程序中,因此在运行时不需要依赖库文件。
2.1 创建静态库的命令
- 编译源代码为目标文件:
gcc -c file1.c # 编译源代码文件为目标文件
gcc -c file2.c # 编译源代码文件为目标文件
该示例会将两个不同名.c文件编译成两个对应文件名.o文件即file1.o和file2.o
- 使用 ar 创建静态库:
ar rcs libmylibrary.a file1.o file2.o # 将目标文件打包成静态库 libmylibrary.a
ar rcs libmylibrary.a *.o # 将目标文件打包成静态库 libmylibrary.a
注:方法二更常用,适用于大工程项目中。
- r:替换库中的文件。
- c:如果库不存在则创建新库。
- s:创建索引文件(加速链接过程)。
2.2 使用静态库
#include "my_stdio.h"
#include "my_string.h"
#include
int main()
{
const char *s = "abcdefg";
printf("%s: %d
", s, my_strlen(s));
mFILE *fp = mfopen("./log.txt", "a");
if(fp == NULL) return 1;
mfwrite(s, my_strlen(s), fp);
mfwrite(s, my_strlen(s), fp);
mfwrite(s, my_strlen(s), fp);
mfclose(fp);
return 0;
}
使用静态库语法格式如下(在程序中链接静态库):
gcc main.c -L. -lmylibrary -o myprogram # 链接静态库 libmylibrary.a
- -L.:指定当前目录为库文件搜索路径。
- -lmylibrary:指定要链接的库(去掉 lib 前缀和 .a 后缀)。
或者:gcc -o usercode usercode.o -L. -lmyc -static
必须存在静态库,可执行程序只会进行静态链接了。
三. 动态库(重点)
动态库一般以.so或.dll为后缀,一般是动态库。
动态库(Dynamic Library)是指在程序运行时才加载并链接的库文件。与静态库不同,动态库不会直接将代码合并到可执行文件中,而是在程序执行时通过操作系统动态加载。动态库通常具有共享特性,可以被多个程序同时使用,减少了内存消耗,并且可以在不重新编译程序的情况下更新库的内容。
3.1 创建动态库的命令
- 编译源代码为共享库(动态库):
gcc -fPIC -shared -o libmylibrary.so file1.c file2.c # 创建动态库libmylibrary.so
- -fPIC:生成与位置无关的代码。
- -shared:生成共享库。
- libmylibrary.so:生成的动态库文件。
- 在程序中链接动态库:
gcc -o myprogram main.c -L. -lmylibrary # 链接动态库libmylibrary.so
- -L.:指定当前目录为库文件搜索路径。
- -lmylibrary:指定要链接的库(去掉 lib 前缀和 .so 后缀)。
注意:动态库的加载
在运行时,操作系统会加载动态库。如果程序在运行时找不到指定的动态库,它会出现加载失败的错误。因此,必须确保动态库文件在可访问的路径下(如环境变量中的路径,或者直接指定库路径)。
3.2 动态库问题
问题1:
ldd a.out
linux-vdso.so.1 => (0x00007fff4d396000)
libmystdio.so => not found
libc.so.6 => /lib64/libc.so.6 (0x00007fa2aef30000)
/lib64/ld-linux-x86-64.so.2 (0x00007fa2af2fe000)
该如何解决呢?
可以看到依赖的动态库中不到。
3.2.1 解决方案
- 拷⻉ .so ⽂件到系统共享库路径下,一般指/usr/lib、/usr/local/lib、/lib64 或者开篇指明的库路径等。
示例:
cp lib/mylib/libmyc.so /lib64
该示例会将自己写的动态库(或者外部库)拷贝至系统的路径下
- 向系统共享库路径下建⽴同名软连接
示例:
ln -s lib/mylib/libmyc.so /lib64/libmyc.so
在 /lib64/ 目录下创建一个名为 libmyc.so 的符号链接,指向 lib/mylib/libmyc.so。这样,当其他程序需要 libmyc.so 时,操作系统会通过这个符号链接找到实际的库文件。
- 更改环境变量: LD_LIBRARY_PATH
示例:
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:动态库的指定路径。
- ldconfig⽅案:配置/etc/ld.so.conf.d/ ,ldconfig更新
过程:创建配置文件,往配置文件中写入动态库的指定路径,同时执行 ldconfig 更新共享库缓存,最后验证配置。 - 验证配置语法格式:
ldconfig -p | grep libmyc
如果配置成功,你应该能在输出中看到你添加的共享库文件(例如 libmyc.so)。
四. 最后
本文详细讲解了库的概念及其在编程中的应用。库(Library)是封装好的可复用代码集合,分为静态库和动态库。静态库在编译时直接合并到可执行文件中,生成较大的文件,而动态库在运行时加载,减少内存占用。文章介绍了如何创建和使用静态库与动态库,包括命令示例及常见问题的解决方案。对于动态库,提到的解决方案包括拷贝 .so 文件到共享库路径、创建符号链接、修改环境变量以及使用 ldconfig 更新共享库缓存。
路虽远,行则将至;事虽难,做则必成
亲爱的读者们,下一篇文章再会!!! color{Red}亲爱的读者们,下一篇文章再会!!! 亲爱的读者们,下一篇文章再会!!!