【Linux】模拟实现FILE以及认识缓冲区
模拟实现FILE以及认识缓冲区
刷新缓冲逻辑图
自定义实现
mystdio.h
#pragma once
#include <stdio.h>
#define NUM 1024
#define BUFF_NOME 0x1
#define BUFF_LINE 0x2
#define BUFF_ALL 0x4
typedef struct _MY_FILE
{
int fd;//接受描述符的值
int flags;//用来记录打开方式
char outputbuffer[NUM];//缓冲区保存
int current;//记录缓冲区有多少字符
}MY_FILE;
MY_FILE* my_fopen(const char* path,const char* mode);
size_t my_fwrite(const void* ptr,size_t size,size_t nmemb,MY_FILE* stream);
int my_fclose(MY_FILE* fp);
int my_fflush(MY_FILE* fp);
mystdio.c
#include "mystdio.h"
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
MY_FILE* my_fopen(const char* path,const char* mode)
{
//1.识别标志位,打开方式
int flag = 0;
if(strcmp(mode,"r") == 0) flag |= O_RDONLY;
else if(strcmp(mode,"w") == 0) flag |= (O_CREAT | O_WRONLY | O_TRUNC);
else if(strcmp(mode,"a") == 0) flag |= (O_CREAT | O_WRONLY | O_APPEND);
else if(strcmp(mode,"r+") == 0) flag |= (O_WRONLY | O_RDONLY);
else if(strcmp(mode,"w+") == 0) flag |= (O_CREAT | O_WRONLY | O_RDONLY | O_TRUNC);
else if(strcmp(mode,"a+") == 0) flag |=(O_CREAT | O_WRONLY | O_RDONLY | O_APPEND);
//2.尝试打开文件
mode_t m = 0666;
int fd = 0;
if(flag | O_CREAT)
{
fd = open(path,flag,m);
}
else
{
fd = open(path,flag);
}
if(fd < 0) return NULL;
//3.给用户返回MY_FILE对象,需要先进行构建
MY_FILE *mf = (MY_FILE*)malloc(sizeof(MY_FILE));
if(mf == NULL)
{
close(fd);
return NULL;
}
//4.初始化MY_FILE对象
mf->fd = fd;
mf->flags = 0;
mf->flags |= BUFF_LINE;
memset(mf->outputbuffer,'',sizeof(mf->outputbuffer));
mf->current = 0;
//5.返回打开的文件
return mf;
}
//冲刷缓冲区
int my_fflush(MY_FILE* fp)
{
assert(fp);
//将用户缓冲区的数据,通过系统调用接口,冲刷给os
write(fp->fd,fp->outputbuffer,fp->current);
fp ->current = 0;
//fsync(fp-fd);
return 0;
}
//这里返回的是字节数,不是模拟实现的输入的、个数nmemb
size_t my_fwrite(const void* ptr,size_t size,size_t nmemb,MY_FILE* stream)
{
//1、缓冲区如果已经满了,就直接写入
if(stream->current == NUM)my_fflush(stream);
//2.根据缓冲区剩余情况,进行数据拷贝即可
size_t user_size = size * nmemb;
size_t my_size = NUM - stream->current;
size_t write = 0;
if(my_size >= user_size)
{
memcpy(stream->outputbuffer + stream->current,ptr,user_size);
//3.更新计数器字段
stream->current += user_size;
write = user_size;
}
else
{
//如果缓冲区内存不够存放的话,指挥存放它的最大值
memcpy(stream->outputbuffer+stream->current,ptr,my_size);
stream->current += my_size;
write = my_size;
}
//4.开始计划刷新,他们高效体现在哪里? --- TODO
//不发生刷新的本质,不进行写入,就是不进行IO,不进行调用系统调用,所以my_fwrite函数调用会非常快,数据会暂时保存在缓冲区中
//可以在缓冲区中挤压多份数据,统一进行刷新写入,本质:就是一次IO可以IO更多的数据,提高IO效率
if(stream->flags & BUFF_ALL)
{
if(stream->current == NUM) my_fflush(stream);
}
else if(stream->flags & BUFF_LINE)
{
if(stream->outputbuffer[stream->current-1] =='n')
{
my_fflush(stream);
}
}
else
{
//TODO
}
return write;
}
int my_fclose(MY_FILE* fp)
{
assert(fp);
//1.冲刷缓冲区
if(fp->current > 0) my_fflush(fp);
//2.关闭文件
close(fp->fd);
//3.释放堆空间
free(fp);
//4.指针置NULL --- 可以设置
fp = NULL;
return 0;
}
main.c
#include "mystdio.h"
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#define MYFILE "log.text"
int main()
{
MY_FILE* fp = my_fopen(MYFILE,"w");
if(fp == NULL) return 1;
const char* str = "hello my my_fwrite";
int cnt = 10;
//操作文件
while(cnt)
{
char buffer[1024];
snprintf(buffer,sizeof(buffer),"%s:%dn",str,cnt--);
size_t size = my_fwrite(buffer,strlen(buffer),1,fp);
sleep(1);
printf("当前成功写入:%lu个字节n",size);
}
my_fclose(fp);
return 0;
}
如何强制刷新内核缓冲区
根据文件描述符进行强制刷新
main.c
#include "mystdio.h"
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#define MYFILE "log.text"
int main()
{
MY_FILE* fp = my_fopen(MYFILE,"w");
if(fp == NULL) return 1;
const char* str = "hello my my_fwrite";
int cnt = 10;
//操作文件
while(cnt)
{
char buffer[1024];
snprintf(buffer,sizeof(buffer),"%s:%dn",str,cnt--);
if(cnt % 5 == 0)
{
//当cnt是五的倍数的时候就会强制刷新一次
my_fwrite(buffer,strlen(buffer),1,fp);
}
}
my_fclose(fp);
return 0;
}
例子
像我们进行scanf输入的时候,其实本身我们输入的是一串字符串,将这个字符串读入对应的缓冲区buff后,然后通过分解工作,进一步传入系统,系统,系统在通过一些指令输入输出想要的结果