内存级文件操作的运用
- 1.模拟实现文件分装
- 2. 深入理解缓冲区的概念
1.模拟实现文件分装
【目标】
以最简单的方式,理解FILE结构体的原理。
头文件:mystdio.h(定义了MY_FILE结构体,声明fopen,fwrite,fclose,fflush函数)
#pragma once
#include<stdio.h>
#define NUM 1024
#define BUFFER_NONE 1
#define BUFFER_LINE 2
#define BUFFER_ALL 4
typedef struct MY_FILE
{
int fd;
int flags;//flush method
char outputbuffer[NUM];
int current;
}MY_FILE;
MY_FILE* my_fpen(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<malloc.h>
#include<unistd.h>
#include<assert.h>
//fopen("a/b/c","a")
//fopen("a/b/c","r")
//fopen("a/b/c","w")
MY_FILE* my_fpen(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
{//other open method
;
}
//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->current=0;
mf->flags=BUFFER_LINE;
memset(mf->outputbuffer,'\0',sizeof(mf->outputbuffer));
//返回打开的文件对象
return mf;
}
size_t my_fwrite(const void *ptr, size_t size, size_t nmemb, MY_FILE *stream)
{
assert(ptr&&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 writen=0;
if(my_size>user_size)
{
memcpy(stream->outputbuffer+stream->current,ptr,user_size);
//3.更新计数器字段
stream->current+=user_size;
writen=user_size;
}
else
{
memcpy(stream->outputbuffer+stream->current,ptr,my_size);
//3.更新计数器字段
stream->current+=my_size;
writen=my_size;
}
//4.开始计划刷新数据,看看他们高效体现在哪里?
//不发生刷新的本质,不进行写入,就是不进行IO,不进行调用系统调用,所以my_fwrite函数调用会非常快,数据会暂时保存在缓冲区中
//可以在缓冲区中挤压多分数据,统一进行刷新写入,本质:就是一次IO可以IO更多的数据,提高IO效率
if(stream->flags&BUFFER_ALL)
{
if(stream->current==NUM)my_fflush(stream);
}
else if(stream->flags&BUFFER_LINE)
{
if(stream->outputbuffer[stream->current-1]=='\n')my_fflush(stream);
}
else
{
//TODO
}
return writen/nmemb;
}
int my_fclose(MY_FILE *fp)
{
assert(fp);
//1.冲刷缓冲区
if(fp->current>0)my_fflush(fp);
//2.关闭文件
close(fp->fd);
//3.释放栈空间
free(fp);
return 0;
}
int my_fflush(MY_FILE* fp)
{
assert(fp);
//将用户缓冲区中的数据,通过系统接口,冲刷给OS
write(fp->fd,fp->outputbuffer,fp->current);
fp->current=0;
fsync(fp->fd);
return 0;
}
main函数
#include"mystdio.h"
#include<string.h>
#include<unistd.h>
#define MYFILE "log.txt"
int main()
{
MY_FILE *fp=my_fpen(MYFILE,"w");
if(fp==NULL)
{
printf("打开文件失败!\n");
return 1;
}
//文件操作
const char *str="hello my fwrite";
int cnt=5;
while(cnt)
{
char buff[1024]="";
snprintf(buff,sizeof(buff),"%s:%d\n",str,cnt--);
size_t size=my_fwrite(buff,strlen(buff),1,fp);
sleep(1);
printf("当前成功写入%lu个字符!\n",size);
}
my_fclose(fp);
return 0;
}
2. 深入理解缓冲区的概念
因为冯诺依曼体系的原因,多次的IO访问外设,速度会很慢。通过缓冲区,可以临时存储数据,再根据缓冲区刷新策略,进行一次IO将数据写入到外设中。IO次数减少,速度明显提高。
缓冲区分成有两种:用户级缓冲区+内核级缓冲区。
用户级别的缓冲区是语言提供的,按照刷新策略调用系统接口,将数据写入到内核级别的缓冲区中;内核级别的缓冲区是不可见的,也有自己的刷新策略,由OS来控制。刷新内核级别的缓冲区才是将数据存储到磁盘上面。