欢迎拜访 :羑悻的小杀马特.-CSDN博客
本篇主题:什么是缓冲区
制作日期:2024.12.11
隶属专栏:linux之旅
目录
一概念:
对应在内存中预留的一段空间;用来缓冲输入或者输出的数据;也就是对应的输入缓冲区和输出缓冲区。
那么为什么要引入缓冲区:
每一次对文件进行的读写操作都需要执⾏⼀次系统调 ⽤,执⾏⼀次系统调⽤将涉及到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_FILE*mfopen(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 <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
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<stdio.h>
#include"mystio.h"
#include <string.h>
#include <unistd.h>
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关闭后自己检查缓冲区是否存在数据完成刷新):
今天的缓冲区小知识到此为止;希望对大家的对此认识能更加清晰,万分感谢!!!
在这篇文章的结尾,我满心感激。感谢您愿意耐心地读完我的文字,您的阅读如同阳光照亮了我创作的道路,愿我们在文字的世界里继续相伴前行,共享更多的美好与感动。