【Linux】文件缓冲区

目录

一、dup2

二、 引入

三、C语言FILE中的缓冲区

3.1 缓冲区的作用

3.2 缓冲区的刷新机制

3.3 对引入代码现象的解释

3.4 模拟实现C语言中的FILE

四、文件系统中的缓冲区

4.1 fsync


在本期内容正式开始之前,我们先介绍一个上期遗漏的知识点:

一、dup2

我们如果要想用代码实现重定向,先要关闭对应的标准流文件,再使用open函数打开需要的文件,来占据原本关闭的标准流的文件描述符,最终实现重定向

还要关闭原本的标准流文件,这样子是不是太麻烦了

下面我们来介绍一个函数dup2(包含在头文件unistd.h中),可以直接替换文件的文件描述符:

int dup2(int oldfd, int newfd);
// dup2() makes newfd be the copy of oldfd, closing newfd first if necessary

● 该函数会将newfd文件描述符所对应的文件,替换成oldfd文件描述符所对应的文件

我们来试试看:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>    
#include<sys/stat.h>    
#include<fcntl.h>    #define LOG "test.txt"int main()
{int fd = open(LOG,O_WRONLY | O_APPEND | O_CREAT , 0666);dup2(fd,1);printf("You can see me\n");    printf("You can see me\n");    printf("You can see me\n");    printf("You can see me\n");close(fd);return 0;
}

运行效果:

二、 引入

我们先来看下面的一段代码:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>    
#include<sys/stat.h>    
#include<fcntl.h>    int main()
{const char* n="Welcome to my blog\n";fprintf(stdout,n);write(1,n,strlen(n));fork();return 0;
}

运行一下: 

咦?怎么重定向了之后多向文件中打印了一个Welcome to my blog?

下面我们来慢慢分析

三、C语言FILE中的缓冲区

3.1 缓冲区的作用

我们知道在C语言中使用文件操作会有FILE*的文件指针,指向一个FILE类型的结构体:

struct _IO_FILE {int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags//缓冲区相关/* The following pointers correspond to the C++ streambuf protocol. *//* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */char* _IO_read_ptr; /* Current read pointer */char* _IO_read_end; /* End of get area. */char* _IO_read_base; /* Start of putback+get area. */char* _IO_write_base; /* Start of put area. */char* _IO_write_ptr; /* Current put pointer. */char* _IO_write_end; /* End of put area. */char* _IO_buf_base; /* Start of reserve area. */char* _IO_buf_end; /* End of reserve area. *//* The following fields are used to support backing up and undo. */char* _IO_save_base; /* Pointer to start of non-current get area. */char* _IO_backup_base; /* Pointer to first valid character of backup area */char* _IO_save_end; /* Pointer to end of non-current get area. */struct _IO_marker* _markers;struct _IO_FILE* _chain;int _fileno; //封装的文件描述符
#if 0int _blksize;
#elseint _flags2;
#endif_IO_off_t _old_offset; /* This used to be _offset but it's too small. */
#define __HAVE_COLUMN /* temporary *//* 1+column number of pbase(); 0 is unknown. */unsigned short _cur_column;signed char _vtable_offset;char _shortbuf[1];/* char* _save_gptr; char* _save_egptr; */_IO_lock_t* _lock;
#ifdef _IO_USE_OLD_IO_FILE
};

在这个结构体中会有文件缓冲区的存在,就是在FILE结构体内部动态开辟的一块空间

这个文件缓冲区有什么用呢?在我们使用fwrite、fread这些函数时,这是一个IO的过程。IO的次数越多,那么时间浪费越多,效率越低。如果使用一个东西预先存储这些数据,当到达一定规模时统一写入文件,那么IO的次数就会减少,进而效率提升。

因此,缓冲区存在的意义就是通过减少IO次数达到效率上的提升

那这个缓冲区什么时候与文件进行数据交换呢?这就涉及到缓冲区的刷新机制了:

3.2 缓冲区的刷新机制

缓冲区共有四种刷新规则:立即刷新(无缓冲)、行刷新(行缓冲)、满刷新(全缓冲)、强制刷新

立即刷新(无缓冲):所谓立即刷新就是字面意义,每当向缓冲区中写入数据就刷新一次。

行刷新(行缓冲):当在数据中检测到换行符'\n'时刷新,这叫行刷新。

比如向显示器printf("hello world\n");时会执行行刷新。

满刷新(全缓冲):当缓冲区中数据已满时刷新,这叫满刷新。

强制刷新:即由人强制缓冲区执行刷新操作,例如fflush就是一种强制刷新。

不同文件对应刷新规则不同,这是主要是由文件使用性质决定的。

对于标准输入输出流(显示器、键盘)来说,其采用行刷新策略。

对于磁盘文件来说,其采用满刷新策略。

3.3 对引入代码现象的解释

在我们第一次运行代码时并没有重定向,所以默认输出流stdout是显示器文件,其对应的缓冲区刷新机制为行刷新(行缓冲),所以在调用fprintf函数时会立即打印传入的字符串

但是在第二次运行时,我们输出重定向了,重定向文件为test.txt,对于磁盘文件来说,其采用满刷新策略。所以在我们调用fprintf函数时不会立即打印传入的字符串(因为这些字符串还不足以将缓冲区填满),不过调用系统级接口write时会立即写入文件(因为系统级接口中并没有缓冲区),在最后fork函数创进程时,子进程会复制父进程的缓冲区。所以在程序最后结束对缓冲区进行刷新时,会写实拷贝出两个缓冲区,两个缓冲区的内容都要刷新到文件中,就造成了最后的现象

3.4 模拟实现C语言中的FILE

下面我们写一个小demo,简单模拟实现一下C语言中的FILE:

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <malloc.h>
#include <unistd.h>
#include <assert.h>#define NUM 1024
#define BUFF_NONE 0x1//无缓冲
#define BUFF_LINE 0x2//行缓冲
#define BUFF_ALL  0x4//全缓冲typedef struct _MY_FILE
{int fd;int flags; //缓冲区的刷新机制char outputbuffer[NUM];//文件缓冲区int  current;//记录文件缓冲区的存贮字符的位数
} MY_FILE;// my_fopen("/a/b/c.txt", "a");
// my_fopen("/a/b/c.txt", "r");
// my_fopen("/a/b/c.txt", "w");
MY_FILE *my_fopen(const char *path, const char *mode)
{//根据传入的mode参数识别标志位int flag = 0;if(strcmp(mode, "r") == 0) flag |= O_RDONLY;else if(strcmp(mode, "w") == 0) flag |= (O_CREAT | O_WRONLY | O_TRUNC);else if(strcmp(mode, "a") == 0) flag |= (O_CREAT | O_WRONLY | O_APPEND);else {//other operator...//"r+", "w+", "a+"}mode_t m = 0666;//创建文件时的权限int fd = 0;//判断是否有O_CREAT标志位来决定打开文件的方式if(flag & O_CREAT) fd = open(path, flag, m);else fd = open(path, flag);if(fd < 0) return NULL;//打开失败//构建MY_FILE对象最后返回MY_FILE *mf = (MY_FILE*)malloc(sizeof(MY_FILE));if(mf == NULL) {close(fd);return NULL;}//初始化MY_FILE对象mf->fd = fd;mf->flags = 0;mf->flags |= BUFF_LINE;//默认所有文件都进行行缓冲memset(mf->outputbuffer, '\0',sizeof(mf->outputbuffer));mf->current = 0;//返回文件对象return mf;
}int my_fflush(MY_FILE *fp)
{assert(fp);//将用户缓冲区中的数据,通过系统调用接口write,冲刷给OSwrite(fp->fd, fp->outputbuffer, fp->current);fp->current = 0;//刷新完置0return 0;
}size_t my_fwrite(const void *ptr, size_t size, size_t nmemb,MY_FILE *stream)
{// 缓冲区如果已经满了,就直接写入if(stream->current == NUM) my_fflush(stream);//根据缓冲区剩余情况,进行数据拷贝即可size_t user_size = size * nmemb;//用户要写入的字节数size_t my_size = NUM - stream->current; //缓冲区剩余空间的字节数size_t writen = 0;//writen记录实际写入的字节数if(my_size >= user_size)//缓冲区剩余容量大于等于用户写入的字节数{memcpy(stream->outputbuffer+stream->current, ptr, user_size);//直接将用户数据拷贝到缓冲区//更新计数器字段stream->current += user_size;writen = user_size;}else//缓冲区剩余容量小于用户写入的字节数{memcpy(stream->outputbuffer+stream->current, ptr, my_size);//剩余多少空间就写入多少字节//更新计数器字段stream->current += my_size;writen = my_size;}//判断刷新机制,根据不同的刷新机制执行不同的方法if(stream->flags & BUFF_ALL)//全缓冲{if(stream->current == NUM) my_fflush(stream);}else if(stream->flags & BUFF_LINE)//行缓冲{if(stream->outputbuffer[stream->current-1] == '\n') my_fflush(stream);}else if(stream->flags & BUFF_NONE)//无缓冲{my_fflush(stream);}return writen;
}int my_fclose(MY_FILE *fp)
{assert(fp);//冲刷缓冲区if(fp->current > 0) my_fflush(fp);//关闭文件close(fp->fd);//释放空间free(fp);fp = NULL;return 0;
}

四、文件系统中的缓冲区

我们在上期博客中也说到过,文件系统中的file结构体也有自己的缓冲区:

那这个缓冲区和C语言中FILE结构体中缓冲区有什么相似之处呢?

我们来看到下图:

我们可以看到,如果我们作为用户调用c标准库中的fopen、fwrite之类的函数,其会将对应FILE结构体中的缓冲区的数据,利用系统调用接口与进程进行交互。 进程再拿着数据交给文件系统中的file结构体中的缓冲区,这个缓冲区的数据最终会被OS刷新到磁盘中(但是对于OS怎么来刷新缓冲区中的数据我们目前还无法弄清楚,它有其自己对应的刷新机制)。

但是系统提供了一个函数接口用来强制刷新文件系统中的缓冲区:fsync

4.1 fsync

#include <unistd.h>
int fsync(int fd);

该函数可以将fd对应的文件描述符中的缓冲区的内容刷新到磁盘中

所以我们可以在之前模拟实现的my_fflush函数中强制刷新一下文件系统中的缓冲区:

int my_fflush(MY_FILE *fp)
{assert(fp);//将用户缓冲区中的数据,通过系统调用接口write,冲刷给OSwrite(fp->fd, fp->outputbuffer, fp->current);fp->current = 0;//刷新完置0fsync(fp->fd);//强制刷新文件系统的缓冲区到磁盘return 0;
}

下期不见不散~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/137465.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构】二叉树的链式实现及遍历

文章目录 一、二叉树的遍历1、前序遍历2、中序遍历3、后序遍历4、层序遍历 二、二叉树结点个数及高度1、二叉树节点个数2、二叉树叶子节点个数3、二叉树第k层节点个数4、二叉树查找值为x的节点 三、二叉树创建及销毁1、通过前序遍历数组创建二叉树2、二叉树的销毁3、判断是否为…

17.适配器模式(Adapter)

意图&#xff1a;将一个类的接口转换为Client希望的另一个接口&#xff0c;使得原本由于接口不兼容而不能一起工作的那些类在一起工作。 UML图 Target&#xff1a;定义Client使用的与特定领域相关的接口。 Client&#xff1a;与符合Target接口的对象协同工作。 Adaptee&#xf…

vue+element plus 使用table组件,清空用户的选择项

<el-table ref"tableRef"> .... </el-table> <script lang"ts" setup> import { onMounted, reactive, ref, nextTick } from vue const clearBtn () > {console.log(清空用户的选择项)tableRef.value.clearSelection() } </scr…

CGI与FastCGI的区别在哪里,FastCGI的应用场景讲解

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈领域优质创作者✌&#xff0c;CSDN博客专家&#xff0c;阿里云社区专家博主&#xff0c;2023年6月CSDN上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师…

qml Combobox用法介绍与代码演示

ComboBox 是 QML 中的一个组件,用于在下拉列表中显示一组项供用户选择。它是 Qt Quick Controls 2 模块中的一个组件,经常用于创建用户界面。 下面是 ComboBox 的一些基本用法: 1. 基本使用: import QtQuick 2.15 import QtQuick.Controls 2.15ApplicationWindow {visib…

在服务器上创建git仓库

1、在服务器上创建git仓库 选择一个创建文件夹的地方&#xff0c;这个地方不会将源码存放在这里&#xff0c;只用于版本控制 # 创建一个专门放置git的文件夹&#xff0c;也可以叫其它名 mkdir git && cd git # 创建自己项目的文件夹&#xff0c;文件夹后面要带 .git…

PBR纹理的10种贴图

PBR 是基于物理的渲染的首字母缩写。它试图通过模拟材料如何吸收和反射光&#xff0c;以模仿现实世界中的光流的方式产生视觉效果。最近的游戏引擎由于其逼真的效果而越来越多地使用 PBR 纹理。对于实时渲染&#xff0c;它们被认为是真实世界场景的最佳近似值。 推荐&#xff…

iOS 17 Simulator Failed with HTTP status 400:bad request

升级 xcode 15 要 ios17 的 sdk 才能运行&#xff0c;但是更新这个 sdk 400 错误了 解决方案&#xff1a; 直接去官网下载开发者后台下载dmg文件&#xff0c;使用命令行快速安装即可 https://developer.apple.com/documentation/xcode/installing-additional-simulator-runti…

c++模板初阶

文章目录 前言一、泛型编程1、泛型编程2、函数模板2.1 函数模板的使用2.2 函数模板的实例化2.3 模板参数的匹配原则 3、类模板 前言 一、泛型编程 1、泛型编程 在学习了前面的c重载之后&#xff0c;我们写一个Swap函数用来交换不同类型的数据时&#xff0c;可以使用函数重载&…

Learn Prompt-Prompt 高级技巧:AutoGPT

AutoGPT 是一个由Toran Richards创建的流行开源项目。它利用GPT4作为大脑&#xff0c;结合langchain的链接思想&#xff0c;连接各种工具和互联网资源&#xff0c;来完成人类给予的任务。您只需要设定一个目标&#xff0c;AutoGPT就会自主规划并逐步执行任务。如果遇到问题&…

xyhcms getshell

下载xyhcms3.6.2021版本并用phpstudy搭建 function get_cookie($name, $key ) {if (!isset($_COOKIE[$name])) {return null;}$key empty($key) ? C(CFG_COOKIE_ENCODE) : $key;$value $_COOKIE[$name];$key md5($key);$sc new \Common\Lib\SysCrypt($key);$value $sc-…

Zabbix5.0_介绍_组成架构_以及和prometheus的对比_大数据环境下的监控_网络_软件_设备监控_Zabbix工作笔记

z 这里Zabbix可以实现采集 存储 展示 报警 但是 zabbix自带的,展示 和报警 没那么好看,我们可以用 grafana进行展示,然后我们用一个叫睿象云的来做告警展示, 会更丰富一点. 可以看到 看一下zabbix的介绍. 对zabbix的介绍,这个zabbix比较适合对服务器进行监控 这个是zabbix的…

PyTorch框架中torch、torchvision、torchaudio与python之间的版本对应关系(9月最新版)

随着python语言和pytorch框架的更新&#xff0c;torch\torchvision\torchaudio与python之间的版本对应关系也在不断地更新。 最新版本torch与torchvision对应关系如下&#xff1a; 稍旧版本torch与torchvision对应关系如下&#xff1a; 最新版本torch与torchaudio对应关系如下…

计算机竞赛 深度学习 机器视觉 车位识别车道线检测 - python opencv

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习 机器视觉 车位识别车道线检测 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f947;学长这里给一个题目综合评分(每项满分5分) …

如何将视频进行分割?这几种分割方法了解一下

当我们将视频分成几段后&#xff0c;可以更好地组织和管理不同的片段&#xff0c;方便后续查找和使用。我们可以根据需要调整视频的长度和内容&#xff0c;满足不同的观看需求。此外&#xff0c;分段视频可以更好地适应不同的观看场景&#xff0c;可以更方便地分享和传播&#…

【网络协议】Http-上

Http请求结构&#xff1a; 结构图1&#xff1a; 实验解析请求报文&#xff1a; 1.在Edge浏览器上输入ip地址端口号文件资源&#xff0c;也就是下图中的120.XX.139.29:8888/A/B/c.html 2.我的程序接收到了一个没有有效载荷的http请求(呼应上面的结构图1)&#xff0c;如下 GET …

三维模型3DTile格式轻量化在数据存储的重要性分析

三维模型3DTile格式轻量化在数据存储的重要性分析 三维模型3DTile格式轻量化在数据存储中占有重要地位。随着科技的不断发展&#xff0c;尤其是空间信息科技的进步&#xff0c;人们对于三维地理空间数据的需求日益增长。然而&#xff0c;这类数据通常具有大尺度、高精度等特点&…

pip pip3安装库时都指向python2的库

当在python3的环境下使用pip3安装库时&#xff0c;发现居然都指向了python2的库 pip -V pip3 -V安装命令更改为&#xff1a; python3 -m pip install <package>

C++跳坑记:位移超出范围的处理

在C编程中&#xff0c;数据类型的选择不仅影响内存占用和性能&#xff0c;还可以对某些操作的结果产生意想不到的影响。今天&#xff0c;我将分享一个关于C在不同变量类型下位移操作结果的发现。 位移操作是C中常见的对整数的高效操作之一。然而&#xff0c;我们可能会忽视一个…

交换机端口镜像详解

交换机端口镜像是一种网络监控技术&#xff0c;它允许将一个或多个交换机端口的网络流量复制并重定向到另一个端口上&#xff0c;以便进行流量监测、分析和记录。通过端口镜像&#xff0c;管理员可以实时查看特定端口上的流量&#xff0c;以进行网络故障排查、安全审计和性能优…