Linux——用户级缓存区及模拟实现fopen、fweite、fclose

linux基础io重定向-CSDN博客


文章目录

目录

文章目录

什么是缓冲区

为什么要有缓冲区

二、编写自己的fopen、fwrite、fclose

1.引入函数

2、引入FILE

3.模拟封装

1、fopen

2、fwrite

3、fclose

4、fflush

总结


前言

用快递站讲述缓冲区

收件区(类比输入缓冲区):

快递站的收件区就如同计算机的输入缓冲区。当快递员不断送来包裹(好比计算机从外部设备接收数据),收件区先把这些包裹暂存起来。就像计算机通过输入设备(如键盘输入字符、网络传来数据等)接收到的数据,不会立刻被全部处理,而是先放在输入缓冲区。例如,很多快递在一天内不同时段被送来,收件区堆积着这些待进一步处理的包裹,这类似输入缓冲区存放着源源不断接收到的数据,等待后续的处理步骤。

分拣区(类比处理缓冲区):

这里可比作计算机的处理缓冲区。在快递站分拣区,工作人员会对包裹进行查看、分类(依据目的地等信息),这类似计算机处理缓冲区对暂存的数据进行分析、判断处理方式。比如要确定数据该送往哪个程序或存储区域进行下一步操作,就如同分拣区确定包裹该发往哪个派送点。而且有时候包裹会在分拣区排队等待进一步转运安排,这也如同数据在处理缓冲区可能会因处理资源不足等原因排队等待处理,以确保按正确顺序完成处理。

发件区(类比输出缓冲区):

快递站的发件区就像计算机的输出缓冲区。分拣好准备发往各个派送点的包裹会在发件区暂存(如同计算机处理好的数据在输出缓冲区暂存),等待快递车辆来运走(类似等待合适时机输出到外部设备)。比如一批要发往同一区域的包裹先在发件区集合,就如同一组要输出到显示器显示的数据先在输出缓冲区等待统一发送以便正确显示。同时,发件区工作人员要协调快递车辆到来时间等,保证包裹有序发出,这类似输出缓冲区要协调与外部设备的连接及数据传输时机,确保数据准确输出到目标设备。

总之,快递站的不同区域通过暂存、处理、再暂存并协调输出的流程,很好地模拟了计算机缓冲区在接收、处理、输出数据过程中的作用。


一、缓冲区是什么?

缓冲区是计算机存储体系中的一个特定区域,主要起到暂存数据的作用。

在输入方面:
当外部设备(如键盘、鼠标、网络等)向计算机输入数据时,数据不会立刻被计算机核心部件(如CPU)处理,而是先进入输入缓冲区暂存。比如你通过键盘快速打字,字符数据会先堆积在输入缓冲区,等待后续按合适的速度和顺序被处理,避免因输入速度过快而导致数据丢失或处理混乱。

在输出方面:
当计算机内部处理好的数据要输出到外部设备(如显示器、打印机等)时,也会先暂存到输出缓冲区。例如计算机要在显示器上显示一幅图像,处理好的图像数据会先放在输出缓冲区,然后再按照显示器的刷新频率等要求,适时且有序地将数据传输到显示器进行显示,这样能保证输出的稳定性和连贯性。

在数据处理过程中:
有时数据在不同部件(如CPU和内存之间、不同程序之间等)流转时,也会在中间设置缓冲区来暂存数据,起到协调数据传输速度差异、避免数据冲突等作用。

总之,缓冲区就像是数据流转过程中的一个个“临时仓库”,让数据的输入、输出和处理都能更顺畅、有序地进行。

什么是缓冲区

缓冲区本质上一块内存区域,用来保存临时数据。缓冲区在各种计算任务中都广泛应用,包括输入/输出操作、网络通信、图像处理、音频处理等。

这块内存区域是由谁提供的呢,缓冲区在哪里呢?可以继续向下看.

这里先告诉答案,是C标准库提供的.

为什么要有缓冲区

缓冲区用于解决数据传输速度不匹配或不稳定的问题,并提高数据处理的效率。

当从硬盘读取大量数据时,将数据直接传输到内存中可能会导致读写速度不匹配(内存速度快,而硬盘读取速度慢,这是相对来说的),从而导致性能瓶颈。为了缓解这个问题,可以引入一个缓冲区,先将一部分数据读取到缓冲区中,然后再从缓冲区逐步读取数据到内存中,以平衡数据传输速度。

这里有个很合适的例子来解释:

例如你和你的朋友在两个不同的大学,相差大概500公里,有一天你想送一些书给你的朋友,此时你可以选择骑自行车,亲自骑行去送这些书,礼轻情意重嘛,加上中途休息,然后由于速度慢,花了大概一周的时间才到,送了之后然后又骑回自己的学校,又花了一周的时间,一共过了两周完整的工作才完成,耗时太长。

假设此时你学聪明了,既然那么慢,那么直接坐高铁去送,可来回一共都500多了,这都比这些书的价值多了,即成本太高了.

可以把以上这些书看做资源,这种模式叫做写透模式.

此时你想到,可以寄快递来送这些书啊,价格便宜,而且两三天就到了,这多实惠,于是你把这些书交给了顺丰 快递,过了两三天,你的朋友在手机上给你说,说我收到这些书了,然后这样就成功的把资源交到了对方的手中。这个顺丰快递在这里扮演的角色便是缓冲区. 

顺丰 拿到你的快递也不是立马就送,而是等待数量足够多时,再一次性开始运输,这相当于是一种缓冲区的刷新策略.

缓冲区刷新策略
刷新策略主要有以下3种:

1.立即刷新

2.行刷新(行缓冲),遇到\n刷新

3.满刷新(全缓冲),指的是将输入或输出的数据完全存储在缓冲区中,然后再进行传输或处理。

当然也会有一些特殊情况:

1.用户强制刷新(fflush)

2.进程退出

遇到以上两种情况时,必须马上从刷新缓冲区的数据,而不要按照之前的刷新策略继续等待.

所以缓冲策略 = 一般情况 + 特殊情况.

一般而言,行缓冲的设备文件 --- 显示器

全缓冲的设备文件 --- 磁盘文件

但所有的设备,永远倾向于全缓冲 --> 缓冲区满了再刷新 --> 需要更少次数的IO操作 -->更少次数的外设访问(相当于提高了整机效率).

有同学可能有疑问,比如10行数据,每一行有100个字节,虽然10行最后再一起刷新,只进行了一次的外设访问,但是数据量很多啊,1000个字节,而按行刷新虽然刷新了10次,但每次数据量少啊,那为什么外设访问次数越少越好呢?

这是因为和外部设备IO的时候,数据量的大小不是主要矛盾,你和外设预备IO的过程是最耗费时间的.

比如你和别人借钱,往往沟通的过程要耗费很长时间,而转账的过程只需要几秒,这同样的道理.

那我们直接改成全缓冲不就行了吗?这样效率不就高了吗,还要什么行缓冲.

其实这些策略,都是根据实际情况做的妥协:

例如行缓冲就是针对于显示器,是给用户看的,一方面要照顾效率,另一方面也要照顾用户体验.

而平常我们打开的一些文本文件便是全缓冲,等到用户全部写完再一次性进行保存.

有了这些缓冲区和策略,便可以提高数据处理的效率. 

二、编写自己的fopen、fwrite、fclose

1.引入函数

这些函数都是3好手册的函数,也就是说这些函数可以有2好手册的系统函数来封装。

2、引入FILE

在之前的语言学习时,我们知道“->"引用通常都是结构体,那么stdin、stdout、stderr这三个流也就是结构体了。

因为IO相关函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件都是通过fd访 问的。

所以C库当中的FILE结构体内部,必定封装了fd。

man 3 stdin,我们可以看到他们的类型。

​编辑

那么FILE又是什么类型呢?

打开头文件找到EILE类

3.模拟封装

我们想要实现一个文件的打开,写入和关闭文件操作的函数。

#include"myfile.h"
#define myfile "test.txt" 
int main()
{
    //打开文件
    _FILE* _fd= _fopen(myfile,"a");
    if(_fd==NULL)return 1;
    //读文件
    const char*ptr="hello linux!!\n";
    //size_t _fwrite(ptr,strlen(ptr),_fd);
    

    int cnt = 10;
    while(cnt){
        _fwrite(ptr, strlen(ptr),_fd);
        // fflush(fp);
        sleep(1);
        cnt--;
    }
    //写关闭文件
    _fclose(_fd); 


    return 0;
}

我们现在就来实现这个几个函数内部封装。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>


#include <string.h>

#define SIZE 1024
#define FILE_MODE 0666
#define FLUSH_NOW 1
#define FLUSH_LINE 2
#define FLUSH_ALL 4

typedef struct _FILE_IO 
{
    int fileno;
    int flag; 
    //char inbuffer[SIZE];
    //int in_pos;
    char outbuffer[SIZE]; // 用一下这个
    int out_pos;
}_FILE;


_FILE* _fopen(const char*path,const char* made);
size_t _fwrite(const char*ptr,size_t size,_FILE* _stream);
int _fclose(_FILE*_stream);

1、fopen

_FILE* _fopen(const char*path,const char* made)
{
    assert(path);
    assert(made);
    int fd=-1;
    int f=0;
       if(strcmp(made, "w") == 0) {
        f = (O_CREAT|O_WRONLY|O_TRUNC);
        fd = open(path, f, FILE_MODE);
    }
    else if(strcmp(made, "a") == 0) {
        f = (O_CREAT|O_WRONLY|O_APPEND);
        fd = open(path, f, FILE_MODE);
    }
    else if(strcmp(made, "r") == 0) {
        f = O_RDONLY;
        fd = open(path, 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;
}

2、fwrite

size_t _fwrite(const char*ptr,size_t size,_FILE* _stream)
{
     memcpy(&_stream->outbuffer[_stream->out_pos], ptr, size); // 没有做异常处理, 也不考虑局部问题
    _stream->out_pos += size;

    if(_stream->flag&FLUSH_NOW)
    {
        write(_stream->fileno, _stream->outbuffer, _stream->out_pos);
        _stream->out_pos = 0;
    }
    else if(_stream->flag&FLUSH_LINE)
    {
        if(_stream->outbuffer[_stream->out_pos-1] == '\n'){ // 不考虑其他情况
            write(_stream->fileno, _stream->outbuffer, _stream->out_pos);
            _stream->out_pos = 0;
        }
    }
    else if(_stream->flag & FLUSH_ALL)
    {
        if(_stream->out_pos == SIZE){
            write(_stream->fileno, _stream->outbuffer, _stream->out_pos);
            _stream->out_pos = 0;
        }
    }

    return size;
}

3、fclose

int _fclose(_FILE*_stream)
{
    if(_stream == NULL) return -1;
    int fd=_stream->fileno;
    _fflush(_stream);
    close(fd);
    free(_stream);
    return fd;

}

4、fflush

void _fflush(_FILE *fp)
{
    if(fp->out_pos > 0){
        write(fp->fileno, fp->outbuffer, fp->out_pos);
        fp->out_pos = 0;
    }
}

完整代码


总结

缓冲区位于用户程序和硬件设备之间,用来缓存数据,目的是让快速的 CPU 不必等待慢速的输入输出设备,同时减少操作硬件的次数。对于 IO 密集型的网络应用程序(如网站、数据库、DNS、CDN 等),缓冲区的设计至关重要,它能十倍甚至一百倍得提高程序性能。
关于缓冲区还有更多的内容,将在 “文件操作” 一章中深入讲解

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

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

相关文章

python学opencv|读取图像

【1】引言 前序学习了使用matplotlib模块进行画图&#xff0c;今天开始我们逐步尝试探索使用opencv来处理图片。 【2】学习资源 官网的学习链接如下&#xff1a; OpenCV: Getting Started with Images 不过读起来是英文版&#xff0c;可能略有难度&#xff0c;所以另推荐一…

多模态大模型打造沉浸式社交体验,Soul App创始人张璐团队海外首秀GITEX GLOBAL

2024年10月14日至18日,全球科技盛会GITEX GLOBAL在迪拜举办,各大科技企业汇聚一堂,展示前沿技术。在这次大会上,中国社交平台Soul App首次亮相国际大型展会,展示了由Soul App创始人张璐团队研发的多模态AI交互方案,吸引了海外来宾的目光。作为国内较早将AI引入社交关系的社交平…

Android 实现悬浮球的功能

Android 实现悬浮球的功能 在 Android 中&#xff0c;实现悬浮球可以通过以下方式实现&#xff0c;常见的方法是使用 WindowManager 创建一个悬浮窗口。以下是具体的实现步骤&#xff1a; 1. 配置权限 在 AndroidManifest.xml 中添加悬浮窗权限&#xff1a; <uses-permis…

Python与Amazon DynamoDB:构建高效爬虫数据存储解决方案

欢迎访问个人博客地址&#xff1a;https://blog.jiumoz.top/archives/pythonyu-amazon-dynamodb-gou-jian-gao-xiao-pa-chong-shu-ju-cun-chu-jie-jue-fang-an 1. 引言 1.1. 爬虫与NoSQL Python爬虫是一种通过模拟浏览器行为&#xff0c;从互联网上自动抓取数据的工具。它利…

Git 进程占用报错-解决方案

背景 大仓库&#xff0c;由于开发者分支较多&#xff0c;我们在使用 git pull 或 git push 等命令时&#xff08;与远端仓库交互的命令&#xff09;&#xff0c;不知之前配置了什么&#xff0c;我的电脑会必现以下报错&#xff08;有非常长一大串报错-不同分支的git进程占用报…

STM32完全学习——使用标准库完成PWM输出

一、TIM2初始化 我这里使用的是STM32F407ZGT6这个芯片&#xff0c;我这里使用的是定时器TIM2来完成PWM输出&#xff0c;由于这里没有使用中断&#xff0c;因此不需要初始化NVIC&#xff0c;下面先来进行定时器的相关初始化 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;R…

【爬虫框架:feapder,管理系统 feaplat】

github&#xff1a;https://github.com/Boris-code/feapder 爬虫管理系统 feaplat&#xff1a;http://feapder.com/#/feapder_platform/feaplat 爬虫在线工具库 &#xff1a;http://www.spidertools.cn &#xff1a;https://www.kgtools.cn/1、feapder 简介 对于学习 Python…

路由引入中次优路由和路由环路问题

A公司用的是IS-IS&#xff0c;B公司用的是OSPF&#xff0c;现在这两个公司要合并&#xff0c;网络要相通 项目目标 前期准备 配置IP地址&#xff1a;完成IP地址规划&#xff0c;A公司和B公司内部网络通过路由器R2和R4环回接口模拟。配置路由器接口的IP地址并测试所有直连链路的…

使用Compose Multiplatform开发跨平台的Android调试工具

背景 最近对CMP跨平台很感兴趣&#xff0c;为了练手&#xff0c;在移动端做了一个Android和IOS共享UI和逻辑代码的天气软件&#xff0c;简单适配了一下双端的深浅主题切换&#xff0c;网络状态监测&#xff0c;刷新调用振动器接口。 做了两年多车机Android开发&#xff0c;偶…

通过LabVIEW项目判断开发环境是否正版

在接收或分析他人提供的LabVIEW项目时&#xff0c;判断其开发环境是否为正版软件对于保护知识产权和避免使用非法软件至关重要。本文将详细介绍如何通过项目文件、可执行程序及开发环境信息判断LabVIEW是否为正版。 ​ 1. 从项目文件判断 LabVIEW项目的源码&#xff08;VI 文件…

React的ts文件中通过createElement拼接一段内容出来

比如接口返回一个值 const values [23.00, 40.00/kg];想做到如下效果&#xff0c; 如果单纯的用render渲染会很简单&#xff0c; 但是在ts文件中处理&#xff0c;所以采用了createElement拼接 代码如下&#xff1a; format: (values: string[]) > {if (!values || !val…

计算机网络 实验七 NAT配置实验

一、实验目的 通过本实验理解网络地址转换的原理和技术&#xff0c;掌握扩展NAT/NAPT设计、配置和测试。 二、实验原理 NAT配置实验的原理主要基于网络地址转换&#xff08;NAT&#xff09;技术&#xff0c;该技术用于将内部私有网络地址转换为外部公有网络地址&#xff0c;从…

002 MATLAB语言基础

01 变量命名规则 变量名只能由字母、数字和下划线组成&#xff0c;且必须以字母开头&#xff1b; 变量名区分字母的大小写&#xff1b; 变量名不能超过最大长度限制&#xff1b; 关键字不能作为变量名&#xff0c;如for、end和if等&#xff1b; 注意&#xff1a;存变量命名时…

无线WiFi网络版毫米波雷达人体传感器,智能家居节能减排照明有人无人识别

在这个科技日新月异的时代&#xff0c;智能家居已经不再是遥不可及的未来概念&#xff0c;而是悄然融入了我们的日常生活&#xff0c;为我们的生活带来了未有的便捷与舒适。今天&#xff0c;让我们一起探索一项创新性的智能家居技术——飞睿智能无线WiFi网络版毫米波雷达人体传…

AI前景分析展望——GPTo1 SoraAI

引言 人工智能&#xff08;AI&#xff09;领域的飞速发展已不仅仅局限于学术研究&#xff0c;它已渗透到各个行业&#xff0c;影响着从生产制造到创意产业的方方面面。在这场技术革新的浪潮中&#xff0c;一些领先的AI模型&#xff0c;像Sora和OpenAI的O1&#xff0c;凭借其强大…

Qwen2.5-7B大模型微调记录

Qwen2.5-7B大模型微调记录 研究需要&#xff0c;需要搞一个大模型出来&#xff0c;没有太多的时间自己训练&#xff0c;准备用现成的开源大模型&#xff0c;然后结合研究方向进行微调 前前后后折腾大半个月&#xff0c;总算做完了第一个微调的大模型&#xff0c;模型基于阿里…

Spring Aop 中对JoinPoint的理解

以下是源码中对 JoinPoint 的描述 A runtime joinpoint is an event that occurs on a static joinpoint (i.e. a location in a program). For instance, an invocation is the runtime joinpoint on a method (static joinpoint). The static part of a given joinpoint can…

netstat -tuln | grep 27017(显示所有监听状态的 TCP 和 UDP 端口,并且以数字形式显示地址和端口号)

文章目录 1. 确定占用端口的进程使用 lsof 命令使用 fuser 命令 2. 结束占用端口的进程3. 修改 MongoDB 配置文件4. 检查 MongoDB 日志文件5. 重新启动 MongoDB 服务6. 检查 MongoDB 服务状态总结 [rootlocalhost etc]# netstat -tuln | grep 27017 tcp 0 0 127.0.…

java脚手架系列16-AI大模型集成

之所以想写这一系列&#xff0c;是因为之前工作过程中有几次项目是从零开始搭建的&#xff0c;而且项目涉及的内容还不少。在这过程中&#xff0c;遇到了很多棘手的非业务问题&#xff0c;在不断实践过程中慢慢积累出一些基本的实践经验&#xff0c;认为这些与业务无关的基本的…

若依框架部署在网站一个子目录下(/admin)问题(

部署在子目录下首先修改vue.config.js文件&#xff1a; 问题一&#xff1a;登陆之后跳转到了404页面问题&#xff0c;解决办法如下&#xff1a; src/router/index.js 把404页面直接变成了首页&#xff08;大佬有啥优雅的解决办法求告知&#xff09; 问题二&#xff1a;退出登录…