什么是缓冲区???

欢迎拜访羑悻的小杀马特.-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关闭后自己检查缓冲区是否存在数据完成刷新):

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

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

相关推荐
A小辣椒1 天前
TShark:Wireshark CLI 功能
linux
A小辣椒1 天前
TShark:基础知识
linux
AlfredZhao1 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao2 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334662 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪2 天前
linux 拷贝文件或目录到指定的位置
linux
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush43 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5203 天前
Linux 11 动态监控指令top
linux
不会C语言的男孩3 天前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言