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