💬 欢迎讨论:如果你在学习过程中有任何问题或想法,欢迎在评论区留言,我们一起交流学习。你的支持是我继续创作的动力! 👍点赞、收藏与分享:觉得这篇文章对你有帮助吗?别忘了点赞、收藏并分享给更多的小伙伴哦!你们的支持是我不断进步的动力! 🚀分享给更多人:如果你觉得这篇文章对你有帮助,欢迎分享给更多对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 <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_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]== '\n')
{
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 != '\0')end++;
return end - s;
}简单模拟实现打开、写、关闭文件,将语言层缓冲区的数据刷新到内核缓冲区,求字符串长度等。
静态库一般以.a或.lib为后缀,一般是静态库。
静态库(Static Library)是编译好的目标文件(.o 或 .obj 文件)的集合,它在编译时直接链接到应用程序中。静态库的内容会在编译过程中与程序的目标文件合并,生成最终的可执行文件。由于静态库在编译时已经链接到程序中,因此在运行时不需要依赖库文件。
gcc -c file1.c # 编译源代码文件为目标文件 gcc -c file2.c # 编译源代码文件为目标文件
该示例会将两个不同名.c文件编译成两个对应文件名.o文件即file1.o和file2.o
ar rcs libmylibrary.a file1.o file2.o # 将目标文件打包成静态库 libmylibrary.a ar rcs libmylibrary.a *.o # 将目标文件打包成静态库 libmylibrary.a 注:方法二更常用,适用于大工程项目中。
#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));
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
或者:gcc -o usercode usercode.o -L. -lmyc -static 必须存在静态库,可执行程序只会进行静态链接了。
动态库一般以.so或.dll为后缀,一般是动态库。
动态库(Dynamic Library)是指在程序运行时才加载并链接的库文件。与静态库不同,动态库不会直接将代码合并到可执行文件中,而是在程序执行时通过操作系统动态加载。动态库通常具有共享特性,可以被多个程序同时使用,减少了内存消耗,并且可以在不重新编译程序的情况下更新库的内容。
gcc -fPIC -shared -o libmylibrary.so file1.c file2.c # 创建动态库libmylibrary.so
gcc -o myprogram main.c -L. -lmylibrary # 链接动态库libmylibrary.so
注意:动态库的加载 在运行时,操作系统会加载动态库。如果程序在运行时找不到指定的动态库,它会出现加载失败的错误。因此,必须确保动态库文件在可访问的路径下(如环境变量中的路径,或者直接指定库路径)。
问题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) 该如何解决呢? 可以看到依赖的动态库中不到。
示例:
cp lib/mylib/libmyc.so /lib64 该示例会将自己写的动态库(或者外部库)拷贝至系统的路径下
ln -s lib/mylib/libmyc.so /lib64/libmyc.so 在 /lib64/ 目录下创建一个名为 libmyc.so 的符号链接,指向 lib/mylib/libmyc.so。这样,当其他程序需要 libmyc.so 时,操作系统会通过这个符号链接找到实际的库文件。
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:动态库的指定路径。
ldconfig -p | grep libmyc 如果配置成功,你应该能在输出中看到你添加的共享库文件(例如 libmyc.so)。
本文详细讲解了库的概念及其在编程中的应用。库(Library)是封装好的可复用代码集合,分为静态库和动态库。静态库在编译时直接合并到可执行文件中,生成较大的文件,而动态库在运行时加载,减少内存占用。文章介绍了如何创建和使用静态库与动态库,包括命令示例及常见问题的解决方案。对于动态库,提到的解决方案包括拷贝 .so 文件到共享库路径、创建符号链接、修改环境变量以及使用 ldconfig 更新共享库缓存。
路虽远,行则将至;事虽难,做则必成