[Linux] Simulate the implementation of FILE and understand the buffer

Simulate the implementation of FILE and understand the buffer

Refresh buffer logic diagram

Insert image description here

Custom implementation

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,'\0',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:%d\n",str,cnt--);
    size_t size = my_fwrite(buffer,strlen(buffer),1,fp);
    sleep(1);
    printf("当前成功写入:%lu个字节\n",size);
  }
    my_fclose(fp);

    return 0;
}

How to force a kernel buffer flush

Force refresh based on file descriptor

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:%d\n",str,cnt--);
    if(cnt % 5 == 0)
    {
    
    
    	//当cnt是五的倍数的时候就会强制刷新一次
    	my_fwrite(buffer,strlen(buffer),1,fp);
    }
  }
    my_fclose(fp);

    return 0;
}

Insert image description here

example

When we perform scanf input, we actually input a string. After reading this string into the corresponding buffer buff, we then further pass it into the system through decomposition work. The system inputs through some instructions. Output the desired results

Guess you like

Origin blog.csdn.net/wh9109/article/details/132461723