浅入缓冲区

简介: 浅入缓冲区

一概念:
对应在内存中预留的一段空间;用来缓冲输入或者输出的数据;也就是对应的输入缓冲区和输出缓冲区。

那么为什么要引入缓冲区:

每一次对文件进行的读写操作都需要执⾏⼀次系统调 ⽤,执⾏⼀次系统调⽤将涉及到CPU状态的切换,即从⽤⼾空间切换到内核空间,实现进程上下⽂的 切换,这将损耗⼀定的CPU时间,频繁的磁盘访问对程序的执⾏效率造成很⼤的影响。

如果加上了缓冲区,那么就减少磁盘的读写次数, 再加上计算机对缓冲区的操作⼤ 快于对磁盘的操作,故应⽤缓冲区可⼤ 提⾼计算机的运⾏速度。

二·缓冲类型:
什么时候刷新:

1.用户强制刷新(fflush后)。

2·满足刷新条件(也就是后面三种缓冲区对应满足的条件)。

3.进程退出。

三种缓冲区类型:

①全缓冲区:当都写满了后;自动刷新到,之后调用系统的IO操作;如磁盘上普通文件的操作。

②行缓冲区:遇到换行符进行刷新;也就是对应的终端操作:显示器输入输出等就是行刷新;默认⾏缓冲区的⼤⼩为1024。

③无缓冲区:标准I/O库不对字符进⾏缓存,直接调⽤系统调⽤,如标准出错流stderr通 常是不带缓冲区的,这使得出错信息能够尽快地显⽰出来。

下面由一张图展示IO库和和系统之间进行文件操作的关系:

三·所谓的FILE:
c语言的一个文件指针类型;其实是一个结构体;它封装了系统调用需要的fd;以及里面还封装了一个自己的缓冲区。

大致结构:

后面我们会模拟实现一个简单的版本。

下面展示一段代码:

第一次执行(直接运行):

第二次执行:

四·简单实现libc库:
下面我们简单的围绕缓冲区这个概念;利用我们上面所设计的知识简单实现一个模拟c库封装的用户级别的文件操作:

模拟实现相关接口函数:mfopen,mfclose,mfflush,mfwrite。

涉及知识:缓冲区,FILE结构体,语言层封装的系统层IO操作。

采用语言:c语言。

我们所谓的FILE其实就是typedef出来的:

这里可以理解为,每次我们利用语言层封装的fopen打开的一个文件类型不就是FILE*;也就是创造了一个这个类型结构体对象。

下面我们来实现上面所说的函数:

mystio.h:

pragma once

define SIZE 1024

define FLUSH_NONE 0

define FLUSH_LINE 1

define FLUSH_FULL 2

struct IO_FILE
{
int fla; //文件访问方式
int fileno; //文件描述符
char outbuffer[SIZE];//缓冲区
int cap;//最大容量
int size;//当前尺寸
int flus;//刷新方式
};
typedef struct IO_FILE mFILE;

struct IO_FILEmfopen(const char filename, const char mode);
int mfwrite(const void
ptr, int num, mFILE stream);
void mfflush(mFILE
stream);
void mfclose(mFILE *stream);

mystio.c:

include"mystio.h"

include

include

include

include

include

include

mFILE buyf(int fileno,int flag){
mFILE
f=(mFILE)malloc(sizeof(mFILE));
if(f == NULL) return NULL;
f->fla=flag;
f->fileno=fileno;
f->flus=FLUSH_LINE;//默认设置成行刷新
f->cap=SIZE;
f->size=0;
memset(f->outbuffer,0,sizeof(f->outbuffer));
return f;
}
mFILE
mfopen(const char filename,const char mode){
umask(0);
int fd=-1;
int flag=0;
if(strcmp(mode,"r")==0){
flag = O_RDWR;
fd = open(filename, flag);
}
else if(strcmp(mode,"w")==0){
flag = O_CREAT | O_WRONLY | O_TRUNC;
fd = open(filename, flag, 0666);
}
else if(strcmp(mode, "a") == 0)
{
flag = O_CREAT | O_WRONLY | O_APPEND;
fd = open(filename, flag, 0666);
}
else{}

   if(fd < 0) return NULL;
   return buyf(fd, flag);

}

int mfwrite(const void ptr, int num, mFILE stream){
//依靠size每次判断好要写入的位置,要么直接写在后面要么遇到换行符,直接刷新出去
//这里清空操作直接用覆盖来代替(也就是把size改成0)
memcpy(stream->outbuffer+stream->size,ptr,num);
stream->size+=num;
if(stream->size>0&&stream->flus== FLUSH_LINE&&stream->outbuffer[stream->size-1]=='\n' )
{
mfflush(stream);
}
return num;
}

void mfflush(mFILE *stream){
if(stream->size>0){
write(stream->fileno,stream->outbuffer,stream->size);//写入内核缓冲区
fsync(stream->fileno);//把内核缓冲区写入磁盘
stream->size=0;
}
}

void mfclose(mFILE *stream){
if(stream->size>0){//关闭前,先看是否用户级缓冲区有数据,先写入内核缓冲区再清空
mfflush(stream);
stream->size=0;

}
close(stream->fileno);

}

main.c:

include

include"mystio.h"

include

include

int main()
{
mFILE *fp = mfopen("log.txt", "a");
if(fp == NULL)
{
return 1;
}
int cnt = 5;
while(cnt)
{
printf("write %d\n", cnt);
char buffer[64];
snprintf(buffer, sizeof(buffer),"hello message, number is : %d\n", cnt);
cnt--;
mfwrite(buffer, strlen(buffer), fp);
//mfflush(fp);
sleep(1);
}
mfclose(fp);
free(stream);
}

下面我们来测试一下:

测试一(每次mfwrite写入的时候以\n结尾,也就是让它实现行刷新):

测试二(每次写入的时候故意不输入\n,而是手动刷新):

测试三(把手动刷新去掉,让它直接都写入缓冲区;最后等到mfclose关闭后自己检查缓冲区是否存在数据完成刷新):

今天的缓冲区小知识到此为止;希望对大家的对此认识能更加清晰,万分感谢!!!

在这篇文章的结尾,我满心感激。感谢您愿意耐心地读完我的文字,您的阅读如同阳光照亮了我创作的道路,愿我们在文字的世界里继续相伴前行,共享更多的美好与感动。

相关文章
|
C语言
C语言malloc与free实现原理
malloc()的实现很简单。它首先会扫描之前由 free()所释放的空闲内存块列表,以求找到尺寸大于或等于要求的一块空闲内存。(取决于具体实现,采用的扫描策略会有所不同。例如,first-fit 或 best-fito。)如果这一内存块的尺寸正好与要求相当,就把它直接返回给调用者。如果是一块较大的内存,那么将对其进行分割,在将一块大小相当的内存返回给调用者的同时,把较小的那块空闲内存块保留在空闲列表中。 如果在空闲内存列表中根本找不到足够大的空闲内存块,那么 malloc()会调用 sbrk()以分配更多
324 0
C语言malloc与free实现原理
|
Java Linux iOS开发
Typora最新版破解, 2022年11.7破解成功
Typora最新版破解, 2022年11.7破解成功, 支持Linux, Windows, Mac三端破解, 你值得拥有哦
|
机器学习/深度学习 人工智能 安全
一篇关于DeepSeek模型先进性的阅读理解
本文以DeepSeek模型为核心,探讨了其技术先进性、训练过程及行业影响。首先介绍DeepSeek的快速崛起及其对AI行业的颠覆作用。DeepSeek通过强化学习(RL)实现Time Scaling Law的新范式,突破了传统大模型依赖算力和数据的限制,展现了集成式创新的优势。文章还提到开源的重要性以及数据作为制胜法宝的关键地位,同时警示了业务发展中安全滞后的问题。
1613 176
一篇关于DeepSeek模型先进性的阅读理解
|
8月前
|
人工智能 缓存 监控
构建高效MCP客户端:应对多服务器环境的完整指南
本文深入探讨了在多服务器环境下构建高效、可靠的Model Context Protocol(MCP)客户端的关键技术与最佳实践。内容涵盖MCP基础架构、连接管理、请求路由、容错机制、会话管理、性能监控及安全认证等核心设计,提供了完整的实现类与部署配置示例,助力开发者构建高性能MCP客户端,提升AI模型与工具集成的效率与稳定性。
|
10月前
|
设计模式 人工智能 Go
go 依赖注入实践
依赖注入(DI)是一种软件设计模式,旨在降低代码耦合度,提高代码可测试性和可复用性。其核心思想是将依赖项从外部传入使用对象,而非由其内部创建。通过 DI,模块间关系更清晰,便于维护和扩展。常见实现包括方法注入和接口注入,适用于如 Go 等支持函数式编程和接口抽象的语言。
230 8
【单片机】简易计算器完整代码带电路图
【单片机】简易计算器完整代码带电路图
1161 0
【单片机】简易计算器完整代码带电路图
|
缓存 负载均衡 算法
C++如何实现一致性算法
一致性哈希是一种用于分布式系统的负载均衡算法,旨在减少服务器增减导致的数据迁移。当有N台服务器时,通过哈希环将请求均匀分布到每台服务器,每台处理N/1的请求。若使用缓存如Redis,可进一步处理高并发场景。算法将哈希值空间视为环形,服务器和请求哈希后定位到环上,按顺时针方向找到第一台服务器作为负载目标。提供的C++代码实现了MD5哈希函数,以及一致性哈希算法的物理节点、虚拟节点和算法本身,以实现节点的添加、删除和请求映射。
271 1
C++如何实现一致性算法
|
开发框架 监控 数据可视化
90%的项目经理都在使用的15款项目管理工具清单
【11月更文挑战第4天】以下是15款广受项目经理欢迎的项目管理工具:Jira、Worktile、Pingcode、Asana、Trello、Basecamp、Monday.com、Smartsheet、ProofHub、VersionOne、Pivotal Tracker、ClickUp、ProjectLibre、OpenProject 和 禅道。这些工具各具特色,适用于不同规模和类型的项目管理需求。
415 3
|
IDE 开发工具 Android开发
eclipse自动补全及其空格键优化(去除空格自动补全)
本文介绍了如何在Eclipse IDE中配置自动补全功能,并提供了去除空格键自动补全代码的技巧。
eclipse自动补全及其空格键优化(去除空格自动补全)
|
存储
好看又规范的Github Readme 制作指南
本文是关于制作规范且外观吸引人的GitHub README文件的指南,包括了README的基本结构、美化技巧,以及如何使用Markdown格式、徽标和图片来增强文档的可读性和吸引力。
1819 0