缓冲区的理解

文章探讨了缓冲区在数据传输中的作用,特别是在C语言中如何通过fwrite等函数将数据从进程拷贝到缓冲区。默认的刷新策略包括立即、行或满缓冲区刷新。当重定向输出时,由于缓冲策略改变,可能导致不同的输出行为。文章还提供了一个自定义的文件函数示例,实现了类似C标准库的缓冲功能,支持不同刷新策略。
摘要由CSDN通过智能技术生成

缓冲区的本质就是一段内存,如果我们要把数据传送给磁盘,并不是直接交给磁盘,由进程交拷贝给缓冲区,这个缓冲区是语言级别的(例如C语言指定的),会定制自己的刷新策略,例如:立即刷新,行刷新,缓冲区满在刷新,但是也有例外,比如:进程退出一般都要刷新缓冲区或者用户强制刷新等等

结论:例如C语言的fwrite理解成拷贝函数才是正确的!将数据从进程拷贝到“缓冲区或者外设中”

在这里插入图片描述

先看一个例子:

#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
int main()
{
  //C接口
  printf("hello printf\n");
  fprintf(stdout,"hello fprintf\n");
  fputs("hello fputs\n",stdout);
  //系统接口
  const char* s="hello write\n";
  write(1,s,strlen(s));
  fork();
  return 0;
}

在这里插入图片描述

这是一个行缓冲,和我上面的结论一致

但是我们重定向的时候系统调用只有一次,C接口的函数有多次

在这里插入图片描述

原因如下:

  • 如果我们没有进行>,看到了4条消息,stdout默认使用的是行刷新,在进行fork之前,三条C函数已经将数据打印输出到显示器上(外设),你的FILE内部,进程不存在对应的数据
  • 如果我们进行了>,写入文件不在是显示器,而是普通文件,采用的刷新策略是全缓冲,之前的3条C显示函数,虽然带了\n,但不足以将stdout缓冲区写满,数据并没有被刷新,执行fork的时候,stdout属于父进程,创建子进程时,紧接就是进程退出,谁先退出,一定要进行缓冲区的刷新(就是改) ,发生写时拷贝,另一个进程随后又会退出,所以是两份
  • write为什么只有一份?上面的过程和write无关,write没有FILE,而用的是fd,就没有C提供的缓冲区

利用之前学的知识,可以写一个属于自己的文件函数,里面封装了系统调用
buffer.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include<assert.h>
#define SYNC_NOW 1 //立即刷新
#define SYNC_LINE 2 //行刷新
#define SYNC_FULL 4 //缓冲区满刷新
#define SIZE 1024
typedef struct FILE_
{
  //缓冲区
  char buffer[SIZE];
  //buffer size
  int size;
  //buffer's actual size
  int capacity;
  //刷新需要把数据写到操作系统里(fileno是打开的文件,用的是fd,而fd返回的是整形)
  int fileno;
  //刷新策略
  int flags;

}FILE_;
FILE_* fopen_(const char* path_name,const char* mode);
void fwrite_(const void* ptr,int num,FILE_* fp);
void fclose_(FILE_* fp);
void fflush_(FILE_* fp);

buffer.c

#include"buffer.h"
FILE_* fopen_(const char* path_name,const char* mode)
{
  int flag=0;
  if(strcmp(mode,"w")==0)
  {
    flag|=(O_WRONLY|O_CREAT|O_TRUNC);
  }
  else if(strcmp(mode,"r")==0)
  {
    flag|=O_RDONLY;
  }
  else if(strcmp(mode,"a")==0)
  {
    flag|=(O_CREAT|O_APPEND|O_WRONLY);
  }
  int fd=0;
  //r打开方式和w\a打开方式打开有区别,w\a需要设置默认权限
  if(flag&O_RDONLY)
  {
    fd=open(path_name,flag);
  }
  else
  {
    fd=open(path_name,flag,0666);
  }
  FILE_* fp=(FILE_*)malloc(sizeof(FILE_));
  assert(fp);
  //指向我们打开的文件
  fp->fileno=fd;
  fp->size=0;
  fp->capacity=SIZE;
  memset(fp->buffer,0,SIZE);
  //默认刷新策略是行刷新
  fp->flags=SYNC_LINE;
  return fp;
}
void fwrite_(const void* ptr,int num,FILE_* fp)
{
  //1.写到缓冲区
  memcpy(fp->buffer+fp->size,ptr,num);
  fp->size+=num;
  //2.判断是否刷新
  if(fp->flags&SYNC_NOW)
  {
    write(fp->fileno,fp->buffer,fp->size);
    //缓冲区size变为0
    fp->size=0;
  }
  else if(fp->flags&SYNC_LINE)
  {
    if(fp->buffer[fp->size-1]=='\n')
    {
      write(fp->fileno,fp->buffer,fp->size);
      fp->size=0;
    }
  }
  else if(fp->flags&SYNC_FULL)
  {
    if(fp->size==fp->capacity)
    {
      write(fp->fileno,fp->buffer,fp->size);
      fp->size=0;
    }
  }
}
void fflush_(FILE_* fp)
{
  write(fp->fileno,fp->buffer,fp->size);
  fp->size=0;
}
void fclose_(FILE_* fp)
{
  fflush_(fp);
  //关闭fd!!!
  close(fp->fileno);
}

test.cpp

#include"buffer.h"
int main()
{
  FILE_* fp=fopen_("log.txt","w");
  const char* s="hello world";
  fwrite_(s,strlen(s),fp);
  //如果不写fclose_即使关闭也没用,因为我们设置了关闭就刷新缓冲区
  fclose_(fp);
  return 0;
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值