自学内容网 自学内容网

什么是缓冲区???

欢迎拜访羑悻的小杀马特.-CSDN博客

本篇主题:什么是缓冲区

制作日期:2024.12.11

隶属专栏:linux之旅

 

 

目录

一概念: 

二·缓冲类型:

三·所谓的FILE:

四·简单实现libc库:


 

一概念: 

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

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

每一次对文件进行的读写操作都需要执⾏⼀次系统调 ⽤,执⾏⼀次系统调⽤将涉及到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关闭后自己检查缓冲区是否存在数据完成刷新): 

 

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

 

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

 


原文地址:https://blog.csdn.net/2401_82648291/article/details/144356662

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!