> 文章列表 > 缓冲区的理解

缓冲区的理解

缓冲区的理解

缓冲区的本质就是一段内存,如果我们要把数据传送给磁盘,并不是直接交给磁盘,由进程交拷贝给缓冲区,这个缓冲区是语言级别的(例如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 sizeint size;//buffer's actual sizeint 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变为0fp->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;
}

缓冲区的理解