【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 4

typedef 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是用来测试的代码,如果有别的代码也可以进行替换使用。


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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

codeforce round951 div2

A guess the maximum 问题&#xff1a; 翻译一下就是求所有相邻元素中max - 1的最小值 代码&#xff1a; #include <iostream> #include <algorithm>using namespace std;const int N 5e4;int a[N]; int n;void solve() {cin >> n;int ans 0x3f3f3f3f;…

贪心算法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;我们称这个整数是单调递增的。…

MySQL普通表转换为分区表实战指南

码到三十五 &#xff1a; 个人主页 引言 本文将详细指导新手开发者如何将MySQL中的普通表转换为分区表。分区表在处理庞大数据集时展现出显著的性能优势&#xff0c;不仅能大幅提升查询速度&#xff0c;还能有效简化数据维护工作。通过掌握这一技巧能够更好地应对数据密集型应…

【Bazel入门与精通】 rules之属性

https://bazel.build/extending/rules?hlzh-cn#attributes Attributes An attribute is a rule argument. Attributes can provide specific values to a target’s implementation, or they can refer to other targets, creating a graph of dependencies. Rule-specifi…

【会议推荐|权威主办】2024年人工智能和机械技术应用国际学术会议 (AIMTA 2024)

2024年人工智能和机械技术应用国际学术会议 &#xff08;AIMTA 2024&#xff09; 2024 International Academic Conference on Artificial Intelligence and Mechanical Technology Applications 【大会信息】 大会地点&#xff1a;西安 大会官网&#xff1a;http://www.icaimt…

springCloudAlibaba之服务熔断组件---sentinel

sentinel组件学习 sentinel学习sentinel容错机制使用代码方式进行QPS流控-流控规则初体验使用SentinelResource注解进行流控 通过代码方式设置降级规则-降级规则初体验sentinel控制台部署客户端整合服务端 springcloud整合sentinelQPS流控规则并发线程数-流控规则BlockExceptio…

kettle从入门到精通 第六十七课 ETL之kettle 再谈kettle阻塞,阻塞多个分支的多个步骤

想真正学习或者提升自己的ETL领域知识的朋友欢迎进群&#xff0c;一起学习&#xff0c;共同进步。由于群内人员较多无法直接扫描进入&#xff0c;公众号后台加我微信入群&#xff0c;备注kettle。 场景&#xff1a;ETL沟通交流群内有小伙伴反馈&#xff0c;如何多个分支处理完…

QT 使用资源文件的注意点

不要存放没有使用的资源文件 即使在代码中没有使用到的资源文件&#xff0c;也会编译到执行文件或者DLL里面去这样会增大它的体积。如下 在代码没有使用这个资源文件(10.4M的2k图片)&#xff0c;但是编译出来的程序有 12M左右的大小 1 假设我们有一个比较复杂的项目&#…

vAttention:用于在没有Paged Attention的情况下Serving LLM

文章目录 0x0. 前言&#xff08;太长不看版&#xff09;0x1. 摘要0x2. 介绍&背景0x3. 使用PagedAttention模型的问题0x3.1 需要重写注意力kernel0x3.2 在服务框架中增加冗余0x3.3 性能开销0x3.3.1 GPU上的运行时开销0x3.3.2 CPU上的运行时开销 0x4. 对LLM服务系统的洞察0x5…

【UML用户指南】-13-对高级结构建模-包

目录 1、名称 2、元素 3、可见性 4、引入与引出 用包把建模元素安排成可作为一个组来处理的较大组块。可以控制这些元素的可见性&#xff0c;使一些元素在包外是可见的&#xff0c;而另一些元素要隐藏在包内。也可以用包表示系统体系结构的不同视图。 狗窝并不复杂&#x…

《python程序语言设计》2018版第5章第35题求完全数,解题经历,我认为的正确代码放在最后

5.35从4月开始一直到成功&#xff0c;此文章将所有的记录和不同阶段代码展现给大家。但是没有配图&#xff0c;我最后成功的代码放在了最后。 2024.04.15 05.35.01version 求完整数&#xff0c;这个让我突然有点蒙。我什么时候能求完整数呢&#xff1f;&#xff1f; 正因子之和…

linux 网桥学习

前言&#xff1a; 本文来学习一下linux网桥概念和网桥配置 1. linux网桥概念 网桥&#xff0c;类似于中继器&#xff0c;连接局域网中两个或者多个网段。它与中继器的不同之处就在于它能够解析它收发的数据&#xff0c;读取目标地址信息&#xff08;MAC&#xff09;&#xff…

QSqlDatabase、QSqlQuery、QSqlRecord、Sqlite用法

使用QSqlDatabase、QSqlQuery、QSqlRecord、Sqlite数据库实现一个简单的界面查询 1. 创建Sqlite数据库&#xff0c;表 mainwindow.cpp #include "mainwindow.h" #include "ui_mainwindow.h" #include "QSqlDatabase" #include "QSqlQuery&q…

ICRA 2024:北京工业大学马楠教授联合中科原动力公司推出番茄采摘自主机器人AHPPEBot,实现32.46秒快速准确采摘

当前&#xff0c;农业生产正深受劳动力短缺困扰&#xff0c;这一现状对生产规模的进一步拓展构成了严重制约。为了突破这一瓶颈&#xff0c;实施自动化已成为提升农业生产力的关键途径&#xff0c;这也使得机器人采收技术备受关注。 现今的机器人采收系统普遍采用先进感知方法&…

31-捕获异常(NoSuchElementException)

在定位元素的时候&#xff0c;经常会遇到各种异常&#xff0c;遇到异常又该如何处理呢&#xff1f;本篇通过学习selenium的exceptions模块&#xff0c;了解异常发生的原因。 一、发生异常 打开百度搜索首页&#xff0c;定位搜索框&#xff0c;此元素id"kw"。为了故意…

我的mybatis学习笔记之二

第一版学习笔记 1,接口是编程: 原生: Dao > DaoImpl mybatis: Mappper > XXXMapper.xml 2,SqlSession代表和数据库的一次会话:用完必须关闭 3,SqlSession和connection一样是非线程安全的.每次使用都必须去获取新的对象 4,mapper接口没有是一类,但是mybtis会为这个接口生…

JVisuaIVM监控Jstatd启动时报错

一、 启动监控Jstatd报错 当我们在windows系统上面启动的时候好好的&#xff0c;在linux上面启动报错&#xff0c;提示报错如下&#xff0c;好像每一什么权限之类的 在tomcat下面查看你的项目使用的java版本&#xff0c;vi /usr/local/tomcat7-8083/bin/catalina.sh 查看我的…

域内攻击 ----> DCSync

其实严格意义上来说DCSync这个技术&#xff0c;并不是一种横向得技术&#xff0c;而是更偏向于权限维持吧&#xff01; 但是其实也是可以用来横向&#xff08;配合NTLM Realy&#xff09;&#xff0c;如果不牵强说得话&#xff01; 那么下面&#xff0c;我们就来看看这个DCSyn…

基于AI大文本模型的智慧对话开发设计及C#源码实现,实现智能文本改写与智慧对话

文章目录 1.AI 大模型发展现状2.基于AI服务的智慧对话开发2.1 大模型API选择2.2 基于C#的聊天界面开发2.3 星火大模型API接入2.4 优化开发界面与显示逻辑 3.源码工程Demo及相关软件下载参考文献 1.AI 大模型发展现状 端午假期几天&#xff0c;关注到国内的AI大模型厂商近乎疯狂…

时序数据库是Niche Market吗?

引言 DB-Engines的流行程度排行从其评估标准[4]可以看出完全不能够做为市场规模的评估标准。甚至于在知道市场规模后可以用这个排行作为一个避雷手册。毕竟现存市场小&#xff0c;可预见增长规模小&#xff0c;竞争大&#xff0c;创新不足&#xff0c;那只能卷价格&#xff0c…