【Linux文件篇】优化文件读写,加速数据处理策略——缓冲区

W...Y的主页 😊

代码仓库分享 💕 


 前言:我们已经复习了C语言中的接口,并且学习了许多文件系统调用,了解了文件描述符以及重定向。今天我们继续学习文件缓冲区的相关内容。

缓冲区

在学习C语言时,我们经常会遇到问题,如果程序没有正确地处理输入缓冲区,可能会导致用户输入的数据没有被完全读取或处理。所以我们要考虑到缓冲区的存在。但是我们却没有真正理解缓冲区是什么,为什么要有缓冲区的存在?

简单来说缓冲区就是一块内存区域,用来存储数据的。为什么要有缓冲区呢?我们先来说一个故事。往前推上30年,那时候应该没有快递或者快递行业还没有普及。当我想给某人一个东西但距离太远时,我们就要驱车前往他所在的城市。这样一趟又费时又费事。现在快递行业普遍,如果再次遇到同样的事情我们的首选肯定是寄快递发送,这样我们的路程可能从几千公里变成了几百米。这样我们既省时又省心。

而快递站就类似缓冲区,我们要记的快递就是数据,这样做大大提高了使用者的效率。而且快递驿站不可能一次只寄你一个人的快递,而是将很多件快递聚集一起一并发出。而缓冲区就是一次将很多数据一并发出,这样做提高了整体的效率。使用空间来换取时间上的效率!!!

在语言层面上,他们一般都自带缓冲区。而调用系统调用是有空间和时间的成本的。所以我们在用户的层面上使用printf、fputs、fwrite都不是直接写到文件里面的,而是先写入到缓冲区中再调用系统调用将缓存区的内容写到磁盘的文件中。

但是操作系统中也是有缓冲区的,因为操作系统中自己有一套缓冲区刷新策略,所以我们不考虑操作系统中的缓冲区,我们默认将文件从语言层面的缓冲区刷入操作系统就写入磁盘了!!!

缓冲区的刷新方式

对于不同的缓冲区,其都有不同的刷新策略,在语言层面有三种刷新策略:
1.无刷新、无缓冲
2.行刷新(一般是往显示器中进行写入操作)
3.全刷新、全刷新(一般是普通文件进行刷新),将缓冲区写满进行刷新。

还有两种特殊情况:

1.强制刷新
2.进程退出刷新

当我们在使用fwrite或fputs函数会发现接口中都有file*指针:

 这些C语言接口中都是file*指针,在系统文件博客中我们说过file*指向的是一个file的结构体,里面有文件描述符等待,那肯定也会有一个缓冲区,所以我们使用fwrite或fputs时只是将内容写入缓冲区中。这样fwrite、fputs函数的效率会大大提升。

FILE结构体源代码:】

typedef struct _IO_FILE FILE; 在/usr/include/stdio.h在/usr/include/libio.h
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 0
int _blksize;
#else
int _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
};

缓冲区存在代码验证

直接显示代码进行验证:

#include<stdio.h>
#include<unistd.h>
#include<string.h>int main()
{const char* s1 = "hello write\n";write(1,s1,strlen(s1));const char* s2 = "hello fwrite\n";fwrite(s2,strlen(s2),1,stdout);const char* s3 = "hello fprintf\n";fprintf(stdout,"%s",s3);//fork();return 0;
}

刚开始我们在代码中没有添加子进程,只是使用一个系统接口write和两个C语言接口fwrite和fprintf往显示器上打印,没有问题,使用重定向往文件中写入也是三行内容没有任何问题。

然后我们使用fork()创建子进程后在向文件中重定向写入,我们可以看到结果:

我们正常运行程序打印三行是没有问题的,但是重定向后再文件中写入却是五行,并且write只有一行其余都是两行。证明write函数只调用了一次,fwrite与fprintf函数都调用了两次。这能说明说明问题呢?

如果向显示器打印是行刷新,无论是否有缓冲区,只要有\n就会进行刷新到显示器上。而向文件中重定向是全刷新,系统调用接口没问题只有一个,而C接口中的内容会先输入到缓冲区中,因为fork会创建出子进程,所以当父进程已经把内容写入缓冲区后再创建子进程,子进程会继承父进程的内容包括缓冲区,而fork后就是结束进程,会刷新缓冲区到操作系统中,所以父子进程的缓冲区都会刷新,从而有两份代码!!

我们就可以证明C语言自带缓冲区。而且缓冲区要支持输入输出的格式化操作。

仿写缓冲区

我们直接通过封装系统调用来仿写一个C语言的缓冲区代码:

mybuffer.h

#ifndef __MYSTDIO_H__
#define __MYSTDIO_H__#include <string.h>#define SIZE 1024#define FLUSH_NOW 1
#define FLUSH_LINE 2
#define FLUSH_ALL 4typedef struct IO_FILE{int fileno;int flag; //char inbuffer[SIZE];//int in_pos;char outbuffer[SIZE]; // 用一下这个int out_pos;
}_FILE;_FILE * _fopen(const char*filename, const char *flag);
int _fwrite(_FILE *fp, const char *s, int len);
void _fclose(_FILE *fp);#endif

mybuffer.c

#include "mybuffer.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>#define FILE_MODE 0666// "w", "a", "r"
_FILE * _fopen(const char*filename, const char *flag)
{assert(filename);assert(flag);int f = 0;int fd = -1;if(strcmp(flag, "w") == 0) {f = (O_CREAT|O_WRONLY|O_TRUNC);fd = open(filename, f, FILE_MODE);}else if(strcmp(flag, "a") == 0) {f = (O_CREAT|O_WRONLY|O_APPEND);fd = open(filename, f, FILE_MODE);}else if(strcmp(flag, "r") == 0) {f = O_RDONLY;fd = open(filename, f);}else return NULL;if(fd == -1) return NULL;_FILE *fp = (_FILE*)malloc(sizeof(_FILE));if(fp == NULL) return NULL;fp->fileno = fd;//fp->flag = FLUSH_LINE;fp->flag = FLUSH_ALL;fp->out_pos = 0;return fp;
}int _fwrite(_FILE *fp, const char *s, int len)
{// "abcd\n"memcpy(&fp->outbuffer[fp->out_pos], s, len); // 没有做异常处理, 也不考虑局部问题fp->out_pos += len;if(fp->flag&FLUSH_NOW){write(fp->fileno, fp->outbuffer, fp->out_pos);fp->out_pos = 0;}else if(fp->flag&FLUSH_LINE){if(fp->outbuffer[fp->out_pos-1] == '\n'){ // 不考虑其他情况write(fp->fileno, fp->outbuffer, fp->out_pos);fp->out_pos = 0;}}else if(fp->flag & FLUSH_ALL){if(fp->out_pos == SIZE){write(fp->fileno, fp->outbuffer, fp->out_pos);fp->out_pos = 0;}}return len;
}void _fflush(_FILE *fp)
{if(fp->out_pos > 0){write(fp->fileno, fp->outbuffer, fp->out_pos);fp->out_pos = 0;}
}void _fclose(_FILE *fp)
{if(fp == NULL) return;_fflush(fp);close(fp->fileno);free(fp);
}

main.c

#include "mybuffer.h"
#include <unistd.h>#define myfile "test.txt"int main()
{_FILE *fp = _fopen(myfile, "a");if(fp == NULL) return 1;const char *msg = "hello world\n";int cnt = 10;while(cnt){_fwrite(fp, msg, strlen(msg));// fflush(fp);sleep(1);cnt--;}_fclose(fp);return 0;
}

main.c是用来测试的代码,如果有别的代码也可以进行替换使用。


以上就是这次的全部内容,我们将已经打开的文件基本已经讲述完毕,下一篇博客我们来系统说说没有被打开的文件,感谢大家观看。

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

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

相关文章

LabVIEW电池测试系统

1. 背景 随着电动汽车、可再生能源等领域的迅速发展&#xff0c;电池作为能源储存和释放的核心组件&#xff0c;其性能评估变得尤为重要。电池的充放电性能、容量、循环寿命等参数直接影响着设备的工作性能和使用寿命。因此&#xff0c;设计一套全面、准确的电池测试系统对于提…

技术前沿 |【大模型BLIP-2的多模态训练】

大模型BLIP-2的多模态训练 一、引言二、BLIP-2模型概述三、多模态训练成本问题四、冻结预训练好的视觉语言模型参数的优势五、冻结预训练好的视觉语言模型参数的方法 一、引言 随着人工智能技术的飞速发展&#xff0c;大型多模态模型如BLIP-2在多个领域取得了显著的成果。然而…

学生信息管理(C语言)

学生信息管理 &#xff08;1&#xff09;问题描述 学生信息包括&#xff1a;学号&#xff0c;姓名&#xff0c;年龄&#xff0c;性别&#xff0c;出生年月&#xff0c;地址&#xff0c;电话&#xff0c;E-mail等。试设计一学生信息管理系统&#xff0c;使之能提供以下功能&am…

[图解]建模相关的基础知识-07

1 00:00:04,710 --> 00:00:08,900 这是划分&#xff0c;下一个是有序对的概念 2 00:00:11,720 --> 00:00:13,800 我们知道集合是不分顺序的 3 00:00:15,090 --> 00:00:18,200 我们花括号来代表集合的话 4 00:00:18,210 --> 00:00:21,000 AB花括号等于BA花括号 …

2_1 Linux基础操作

2_1 Linux基础操作 文章目录 2_1 Linux基础操作0. 参考1. 装机后的一些小命令查看系统的信息2. 基础命令2.1 初识基本命令2.2 日期和时间 3. 帮助命令4. 关机、重启5. 设置主机名6. rm删除7. 软件包的管理RPM、 YUM8. IP知识9. 查看一些linux的信息10. 命令行快捷键11. 光盘挂载…

数据可视化——pyecharts库绘图

目录 官方文档 使用说明&#xff1a; 点击基本图表 可以点击你想要的图表 安装&#xff1a; 一些例图&#xff1a; 柱状图&#xff1a; 效果&#xff1a; 折线图&#xff1a; 效果&#xff1a; 环形图&#xff1a; 效果&#xff1a; 南丁格尔图&#xff08;玫瑰图&am…

贪心算法06(leetcode738,968)

参考资料&#xff1a; https://programmercarl.com/0738.%E5%8D%95%E8%B0%83%E9%80%92%E5%A2%9E%E7%9A%84%E6%95%B0%E5%AD%97.html 738. 单调递增的数字 题目描述&#xff1a; 当且仅当每个相邻位数上的数字 x 和 y 满足 x < y 时&#xff0c;我们称这个整数是单调递增的。…

CTF-PWN-kernel-UAF

文章目录 参考slub 分配器kmem_cache_cpukmem_cache_node[ ]冻结和解冻分配释放 fork绑核Kmalloc flag和slub隔离CISCN - 2017 - babydriver检查babtdriver_initstruct cdevalloc_chrdev_regioncdev_initownercdev_add_class_createdevice_create babyopenbabyreleasebabyreadb…

【C++】STL中list的使用

前言&#xff1a;在前面学习的 过程中我们学习了STL中的string,vector&#xff0c;今天我们来进一步的学习STL中的list的使用方法。 &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x1f49e; &#x1f449; 专栏分类:高质量&#xff23;学习 &#x1f448; &#x1f4af;代…

全志D1s软件入门之Tina Linux烧写教程

烧写 Tina Linux 烧写&#xff0c;即将编译打包好的固件下载到设备 烧写方式简介 全志平台为开发者提供了多种多样的烧写方式和烧写工具&#xff1a; &#xff08;1&#xff09; PhoenixSuit&#xff1a;基于Windows的系统的烧写工具&#xff0c;是最常用的烧写工具&#x…

树莓派4b安装宝塔面板

1、打开命令窗口&#xff0c;执行如下命令 #更新 sudo apt-get update sudo apt-get upgrade #切换root权限 sudo su root #安装宝塔面板 wget -O install.sh http://download.bt.cn/install/install-ubuntu_6.0.sh && bash install.sh安装过程有点久&#xff0c;会持…

01——生产监控平台——WPF

生产监控平台—— 一、介绍 VS2022 .net core(net6版本&#xff09; 1、文件夹&#xff1a;MVVM /静态资源&#xff08;图片、字体等&#xff09; 、用户空间、资源字典等。 2、图片资源库&#xff1a; https://www.iconfont.cn/ ; 1.资源字典Dictionary 1、…

C++系统编程篇——linux编译器 gcc/g++(链接动静态库)

linux编译器-gcc/g &#xff08;1&#xff09;g安装&#xff08;gcc一般自带&#xff0c;g需要下载&#xff09; sudo yum install -y gcc-c g --version gcc用于编译C语言代码&#xff0c;g用于编译C代码 &#xff08;2&#xff09;程序翻译过程 选项“-o”是指目标文件…

Python的return和yield,哪个是你的菜?

目录 1、return基础介绍 &#x1f4da; 1.1 return用途&#xff1a;数据返回 1.2 return执行&#xff1a;函数终止 1.3 return深入&#xff1a;无返回值情况 2、yield核心概念 &#x1f347; 2.1 yield与迭代器 2.2 生成器函数构建 2.3 yield的暂停与续行特性 3、retur…

MFC 教程-回车时窗口退出问题

【问题描述】 MFC窗口默认时&#xff0c;按回车窗口会退出 【原因分析】 默认调用OnOK() 【解决办法】 重写虚函PreTranslateMessage BOOL CTESTMFCDlg::PreTranslateMessage(MSG* pMsg) {// TODO: 在此添加专用代码和/或调用基类// 修改回车键的操作反应 if (pMsg->…

C++11新特性【上】(统一的列表初始化、auto、decltype、右值引用、万能引用、完美转发)

一、C11简介 在2003年C标准委员会曾经提交了一份技术勘误表(简称TC1)&#xff0c;使得C03这个名字已经取代了 C98称为C11之前的最新C标准名称。不过由于C03(TC1)主要是对C98标准中的漏洞 进行修复&#xff0c;语言的核心部分则没有改动&#xff0c;因此人们习惯性的把两个标准合…

高德地图简单实现点标,和区域绘制

高德地图开发文档:https://lbs.amap.com/api/javascript-api/guide/abc/quickstart 百度搜索高德地图开发平台 注册高德地图开发账号 在应用管理中 我的应用中 添加一个Key 点击提交 进入高德地图开发文档:https://lbs.amap.com/api/javascript-api/guide/abc/quickstart …

Mysql 的分布式策略

1. 前言 MySQL 作为最最常用的数据库&#xff0c;了解 Mysql 的分布式策略对于掌握 MySQL 的高性能使用方法和更安全的储存方式有非常重要的作用。 它同时也是面试中最最常问的考点&#xff0c;我们这里就简单总结下 Mysq 的常用分布式策略。 2. 复制 复制主要有主主复制和…

使用 C# 学习面向对象编程:第 2 部分

C# 类属性简介 属性在面向对象编程中起着至关重要的作用。它们允许我们从类外部访问类的私有变量。在类中使用私有变量是很好的。属性看起来像变量和方法的组合。属性有部分&#xff1a;“get 和 set”方法。get 方法应该返回变量&#xff0c;而 set 方法应该为其赋值。 步骤…

Chat-TTS:windows本地部署实践【有手就行】

最近Chat-TTS模型很火&#xff0c;生成的语音以假乱真&#xff0c;几乎听不出AI的味道。我自己在本地部署玩了一下&#xff0c;记录一下其中遇到的问题。 环境&#xff1a; 系统&#xff1a;windows 11 GPU&#xff1a; Nvidia 4060 Cuda&#xff1a;12.1&#xff08;建议安…