【Linux】缓冲区与缓冲区的刷新策略


目录

1.缓冲区基础

1.1缓冲区的刷新策略

1.1.1三种刷新策略

1.1.2.两种强制刷新策略

2.用户级语言层缓冲区

2.1.默认在显示器输出

2.2.重定向到文件输出

2.3.write调用没有显示两份的原因

3.模拟实现文件缓冲区

[3.1 myFileBuffer.h](#3.1 myFileBuffer.h)

[3.2 myFileBuffer.c](#3.2 myFileBuffer.c)

4.系统内核缓冲区

最后


1.缓冲区基础

缓冲区本身就是一段内存,就是用来执行做缓存的一段内存空间。

内存对磁盘进行存储信息,称为外设IO,速度很慢

缓冲区 是,直接在内存中开辟一段空间,将数据拷贝在缓存区中,在缓存区的数据会定期刷新给磁盘,有效的节省了进程进行数据IO的时间。

1.1缓冲区的刷新策略

缓冲区会结合具体的设备执行自己的刷新策略。

1.1.1三种刷新策略

1.立即刷新 无缓冲

2.行刷新 行缓冲 显示器(显示器设备特殊符合人类的阅读习惯,按行缓存提升效率不至于太低

3.缓冲区刷新 全缓冲 磁盘文件 (效率最高,定期一次刷新出去

1.1.2.两种强制刷新策略

1.用户强制刷新 fflush()

2.进程退出 ,一般都要进行缓冲区刷新


2.用户级语言层缓冲区

这个缓冲区在C语言提供的FILE结构体中

测试代码:

cpp 复制代码
  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<string.h>
  4 
  5 int main()
  6 {
  7   printf("hello printf\n");
  8   fprintf(stdout,"hello fprintf\n");
  9   const char* fputsString="hello fputs\n";
 10   fputs(fputsString,stdout);
 11 
 12   //系统接口
 13   const char*  writeString="hello write\n";                  
 14   write(1,writeString,strlen(writeString));
 15                                            
 16   fork();
 17          
 18   return 0;
 19 }          
 20  

在显示器上输出

重定向到文件输出

两种输出方式结果不同,其中重定向到文件输出的内容,比直接到显示器输出的内容c语言的接口多输出了一遍,是因为文件和显示器的缓冲区刷新策略不同。

在代码结束之前,先创建了子进程

2.1.默认在显示器输出

显示器的刷新策略是行缓冲刷新,在进程fork之前,三条c语言的内容已经被行刷新到显示器上。在fork后其FILE内部已经不存在对应的数据。

2.2.重定向到文件输出

文件的刷新策略是全缓冲,之前的显示函数虽然带了\n,但是不足以把文件的缓冲区写满,此时数据并没有刷新。

在执行fork时,创建子进程,紧接着就是进程退出,进程退出默认刷新缓冲区。此时发生写时拷贝,后面退出的进程会再刷新一份,所以最终数据显示了两份。

2.3.write调用没有显示两份的原因

write是系统调用接口,上面的过程都是采用的用户级语言层面给我们提供的缓冲区,write使用的不是FILE而是fd,更加底层。


3.模拟实现文件缓冲区

通过myFileBuffer.h和myFileBuffer.c两个文件,模拟实现语言级文件缓冲区FILE

3.1 myFileBuffer.h

cpp 复制代码
  1 #pragma once
  2 #include<string.h>
  3 #include<sys/types.h>
  4 #include<sys/stat.h>
  5 #include<fcntl.h>
  6 #include<unistd.h>
  7 #include<assert.h>
  8 #include<errno.h>
  9 #include<stdlib.h>
 10 
 11 #define SIZE 1024                                                                                                                                            
 12           
 13 //刷新方式        
 14 #define SYNC_NOW 1 
 15 #define SYNC_LINE 2
 16 #define SYNC_FULL 4
 17                      
 18 typedef struct FILE_{ 
 19   int flags;//刷新方式   
 20   int fileno;//文件描述符
 21   int cap;//buffer总容量       
 22   int size;//buffer当前的使用量
 23   char buffer[SIZE];
 24 }_FILE;
 25                                                     
 26 _FILE* fopen_(const char*path_name,const char*mode);
 27 void fwrite_(const void *ptr,int num,_FILE *fp);
 28 void fclose_(_FILE* fp);
 29 void fflush_(_FILE *fp);

3.2 myFileBuffer.c

cpp 复制代码
  1 #include"myFileBuffer.h"                                                                     
  2                                                                                                    
  3 _FILE* fopen_(const char*path_name,const char*mode){                                                  
  4                                                                                                      
  5   int flags=0;                                                                                    
  6   int defaultMode=0666;                                                                            
  7                                                                                                    
  8   //判断不同的打开方式设置flags 这里举例w a r                                                     
  9   if(strcmp(mode,"r")==0)                                                                          
 10   {                                                                              
 11     flags=O_RDONLY;                                                         
 12                                                                                  
 13   }                                                                                        
 14   else if(strcmp(mode,"w")==0)                                                                     
 15   {                                                                                                 
 16     flags=O_WRONLY|O_CREAT|O_TRUNC;                                                                 
 17   }                                                                              
 18   else if(strcmp(mode,"a")==0)                                                                        
 19   {                                                                                                    
 20     flags=O_WRONLY|O_CREAT|O_APPEND;                                                                      
 21   }                                                                                                      
 22   else{                                                                                                         
 23                                                                                                      
 24   }   
 26   //根据不同的打开方式,打开文件                                                                                                     
 27   int fd=0;                                                                                                                      
 28   if(flags&O_RDONLY)
 29   {                                                                                                      
 30     fd=open(path_name,flags);                                                                                                                                
 31   }                                                                              
 32   else{                                                                     
 33     fd=open(path_name,flags,defaultMode);                                   
 34   }
 35 
 36   //printf("我的fd是 %d\n",fd);
 37   //如果打开失败
 38   if(fd<0)
 39   {
 40     const char* err=strerror(errno);
 41     write(2,err,strlen(err));
 42     return NULL;
 43   }
 44 
 45   //创建文件缓冲区并对其初始化
 46   _FILE*fp=(_FILE*)malloc(sizeof (_FILE));
 47   assert(fp);
 48   fp->flags=SYNC_LINE;//默认设置成行刷新
 49   fp->fileno=fd;
 50   fp->cap=SIZE;
 51   fp->size=0;
 52   memset(fp->buffer,0,SIZE);//初始化文件缓冲区
 53 
 54   return fp;
 55 }
 56 
 57 void fwrite_(const void *ptr,int num,_FILE *fp)                                                                                                              
 58 {
 59   //写到文件缓冲区中
 60   memcpy(fp->buffer+fp->size,ptr,num);//注意这里的起始地址
 61   fp->size+=num;
 62 
 63 //  printf("fd->fileno是 %d\n",fp->fileno);
 64 
 65   //判断是否刷新
 66   if(fp->flags&SYNC_NOW)
 67   {
 68     //立即刷新
 69     write(fp->fileno,fp->buffer,fp->size);
 70     fp->size=0;
 71   }
 72   else if(fp->flags&SYNC_FULL)
 73   {
 74     //等到缓冲区写满再刷新
 75     if(fp->size==fp->cap)
 76     {
 77       write(fp->fileno,fp->buffer,fp->size);
 78       fp->size=0;
 79     }
 80   }
 81   else if(fp->flags&SYNC_LINE)
 82   {
 83     if(fp->buffer[fp->size-1]=='\n')
 84     {                                                                                                                                                        
 85    //   printf("这里是行缓冲\n");
 86 
 87       write(fp->fileno,fp->buffer,fp->size);
 88       fp->size=0;
 89       //把_FILE缓冲区的内容拷贝到内核缓冲区中
 90     } 
 91   }
 92   else{
 93 
 94   }
 95 
 96 }
 97 void fclose_(_FILE* fp)
 98 {
 99   //文件退出前会强制刷新
100   fflush_(fp);
101   close(fp->fileno);
102 }
103 void fflush_(_FILE *fp)
104 {
105   //强制刷新
106   if(fp->size>0)
107   {
108     write(fp->fileno,fp->buffer,fp->size);
109     fp->size=0;
110   }
111 }

                                     

4.系统内核缓冲区

os的刷新策略很复杂,是权衡自己整体的内存使用情况来进行相应的刷新,并不是上文所讲述的简单的刷新方法。这与用户无关。

整体过程就是:

把内容由用户代码拷贝到 C语言的缓冲区中,再由C语言缓冲区拷贝到内核缓冲区,再有内核缓冲区刷新到外设。


最后

加油

相关推荐
UpUpUp……25 分钟前
Linux--JsonCpp
linux·运维·服务器·c++·笔记·json
Willis_m42 分钟前
Linux 服务器用 SSH 拉取多个 Git 工程
linux·服务器·git·ssh
紫金修道1 小时前
【Linux】在Arm服务器源码编译onnxruntime-gpu的whl
linux·服务器·arm开发
Clockwiseee1 小时前
文件上传总结
运维·服务器·学习·文件上传
苜柠2 小时前
Wpf学习片段
学习
欢乐熊嵌入式编程2 小时前
智能手表固件升级 OTA 策略文档初稿
嵌入式硬件·学习·智能手表
xq5148632 小时前
Linux系统下安装mongodb
linux·mongodb
柒七爱吃麻辣烫2 小时前
在Linux中安装JDK并且搭建Java环境
java·linux·开发语言
起床学FPGA2 小时前
异步FIFO的学习
学习·fpga开发
依年南台3 小时前
搭建大数据学习的平台
大数据·学习