深入理解C语言对文件的包装与缓冲区

news2024/11/13 12:53:15

内存级文件操作的运用

  • 1.模拟实现文件分装
  • 2. 深入理解缓冲区的概念

1.模拟实现文件分装

【目标】
以最简单的方式,理解FILE结构体的原理。

头文件:mystdio.h(定义了MY_FILE结构体,声明fopen,fwrite,fclose,fflush函数)

#pragma once 

#include<stdio.h>

#define NUM 1024
#define BUFFER_NONE 1
#define BUFFER_LINE 2
#define BUFFER_ALL 4

typedef struct MY_FILE
{
  int fd;
  int flags;//flush method
  char outputbuffer[NUM];
  int current;
}MY_FILE;


MY_FILE* my_fpen(const char *path, const char *mode);
size_t my_fwrite(const void *ptr, size_t size, size_t nmemb, MY_FILE *stream);
int my_fclose(MY_FILE *fp);
int my_fflush(MY_FILE *fp);

mystdio.c文件:相关函数的实现

#include"mystdio.h"
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<malloc.h>
#include<unistd.h>
#include<assert.h>

//fopen("a/b/c","a")
//fopen("a/b/c","r")
//fopen("a/b/c","w")
MY_FILE* my_fpen(const char *path, const char *mode)
{
  //1.识别标志位,确定打开方式
  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 open method
    ;
  }
  //2.尝试打开文件
  mode_t m=0666;
  int fd=0;
  if(flag&O_CREAT)fd=open(path,flag,m);
  else  fd=open(path,flag);

  if(fd<0)return NULL;
  //3.给用户返回MY_FILE对象,需要先构建
  MY_FILE* mf=(MY_FILE*)malloc(sizeof(MY_FILE));
  if(mf==NULL)
  {
    close(fd);
    return NULL;
  }
  
  //4.初始化MY_FILE对象
  mf->fd=fd;
  mf->current=0;
  mf->flags=BUFFER_LINE;
  memset(mf->outputbuffer,'\0',sizeof(mf->outputbuffer));

  //返回打开的文件对象
  return mf;
}
size_t my_fwrite(const void *ptr, size_t size, size_t nmemb, MY_FILE *stream)
{
  assert(ptr&&stream);
  //1.缓冲区如果已经满了,直接写入
  if(stream->current==NUM)my_fflush(stream);

  //2.根据缓冲区剩余情况,进行数据拷贝
  size_t user_size=size*nmemb;
  size_t my_size = NUM -stream->current;
  
  size_t writen=0;
  if(my_size>user_size)
  {
    memcpy(stream->outputbuffer+stream->current,ptr,user_size);
    //3.更新计数器字段
    stream->current+=user_size;
    writen=user_size;
  }
  else 
  {
    memcpy(stream->outputbuffer+stream->current,ptr,my_size);
    //3.更新计数器字段
    stream->current+=my_size;
    writen=my_size;
  }

  //4.开始计划刷新数据,看看他们高效体现在哪里?
  //不发生刷新的本质,不进行写入,就是不进行IO,不进行调用系统调用,所以my_fwrite函数调用会非常快,数据会暂时保存在缓冲区中
  //可以在缓冲区中挤压多分数据,统一进行刷新写入,本质:就是一次IO可以IO更多的数据,提高IO效率
  if(stream->flags&BUFFER_ALL)
  {
    if(stream->current==NUM)my_fflush(stream);
  }
  else if(stream->flags&BUFFER_LINE)
  {
    if(stream->outputbuffer[stream->current-1]=='\n')my_fflush(stream);
  }
  else 
  {
    //TODO
  }

  return writen/nmemb;
}
int my_fclose(MY_FILE *fp)
{
  assert(fp);
  //1.冲刷缓冲区
  if(fp->current>0)my_fflush(fp);
  //2.关闭文件
  close(fp->fd);
  //3.释放栈空间
  free(fp);
  return 0;
}

int my_fflush(MY_FILE* fp)
{
  assert(fp);
  //将用户缓冲区中的数据,通过系统接口,冲刷给OS
  write(fp->fd,fp->outputbuffer,fp->current); 
  fp->current=0;

  fsync(fp->fd);
  return 0;
}

main函数

#include"mystdio.h"
#include<string.h>
#include<unistd.h>


#define MYFILE "log.txt"

int main()
{
  MY_FILE *fp=my_fpen(MYFILE,"w");
  if(fp==NULL)
  {
    printf("打开文件失败!\n");
    return 1;
  }
  //文件操作
  const char *str="hello my fwrite";
  int cnt=5;
  while(cnt)
  {
    char buff[1024]="";
    snprintf(buff,sizeof(buff),"%s:%d\n",str,cnt--);
    size_t size=my_fwrite(buff,strlen(buff),1,fp);
    sleep(1);
    printf("当前成功写入%lu个字符!\n",size);
  }

  my_fclose(fp);
  return 0;
}

2. 深入理解缓冲区的概念

因为冯诺依曼体系的原因,多次的IO访问外设,速度会很慢。通过缓冲区,可以临时存储数据,再根据缓冲区刷新策略,进行一次IO将数据写入到外设中。IO次数减少,速度明显提高。

缓冲区分成有两种:用户级缓冲区+内核级缓冲区。

用户级别的缓冲区是语言提供的,按照刷新策略调用系统接口,将数据写入到内核级别的缓冲区中;内核级别的缓冲区是不可见的,也有自己的刷新策略,由OS来控制。刷新内核级别的缓冲区才是将数据存储到磁盘上面。
在这里插入图片描述

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

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

相关文章

『手撕 Mybatis 源码』10 - 一级缓存

一级缓存 概述 一级缓存是 SqlSession 级别的缓存。在操作数据库时需要构造 SqlSession 对象&#xff0c;在对象中有一个数据结构&#xff08;HashMap&#xff09;用于存储缓存数据。不同的 SqlSession 之间的缓存数据区域&#xff08;HashMap&#xff09;是互相不影响的二级缓…

hive基于新浪微博的日志数据分析——项目及源码

有需要本项目的全套资源资源以及部署服务可以私信博主&#xff01;&#xff01;&#xff01; 本系统旨在对新浪微博日志进行大数据分析&#xff0c;探究用户行为、内容传播、移动设备等多个方面的特征和趋势&#xff0c;为企业和个人的营销策略、产品设计、用户服务等提供有益的…

PN结、二极管、三极管、三极管放大电路、上拉电路/下拉电路

1、N型参杂 与 P型参杂 B站 视频地址 &#xff1a;https://www.bilibili.com/video/BV1fB4y147Gn 1&#xff09;N型参杂 &#xff08;N型半导体&#xff09; &#xff1a; 4价硅 参杂 5价麟&#xff0c;多一个自由负电子&#xff08;带负电&#xff09; 2&#xff09;P型参杂…

Linux性能学习(4.2):网络_为什么MTU是1500

文章目录 1 基本概念2 为什么MTU是15003 有效载荷最大是1500吗4 Linux下如何修改MTU 参考资料&#xff1a; 1. RFC894 2. 什么是MTU&#xff08;Maximum Transmission Unit&#xff09;&#xff1f; 1 基本概念 Maximum Transmission Unit&#xff0c;缩写MTU&#xff0c;即…

Python爬虫:Scrapy框架

&#x1f680;Python爬虫&#xff1a;Scrapy框架 &#x1f577;️ Scrapy介绍&#x1f4e6; Scrapy框架&#x1f4c1; Scrapy项目&#x1f50d; 创建爬虫过程&#x1f578;️ 页面分析&#x1f4d1; 提取信息&#x1f389; 完整代码&#x1f4dd; 结语 在本篇博文中&#xff0c…

C++6.29思维,作业

有以下类定义&#xff0c;按要求实现剩余功能 #include <iostream> using namespace std;class Person { private:int age;int *p; public://无参构造Person():p(new int(89)){age 18;cout << "无参构造" << endl;}//有参构造Person(int age,int …

docker的容器

首先要关闭防火墙,不然会阻止连接 查询防火墙状态 systemctl status firewalld 如果是running的状态要关闭一下 关闭防火墙 systemctl stop firewalld 禁用防火墙(禁止开机启动) systemctl disable firewalld 容器的创建语句: docker run …

第11节 跟上板块轮动的节奏

板块 文章目录 板块什么是板块板块的分类板块的轮动 板块相关接口本节课任务 什么是板块 股票板块是一些具有相同特征的股票的集合&#xff0c;命名通常也会简单明了的直接按照特征命名。例如沪深300板块&#xff0c;蓝筹板块。对上市公司进行“分班”不论是对于企业还是对于投…

Leetcode-每日一题【148.排序链表】

题目 给你链表的头结点 head &#xff0c;请将其按 升序 排列并返回 排序后的链表 。 示例 1&#xff1a; 输入&#xff1a;head [4,2,1,3]输出&#xff1a;[1,2,3,4] 示例 2&#xff1a; 输入&#xff1a;head [-1,5,3,4,0]输出&#xff1a;[-1,0,3,4,5] 示例 3&#xff1…

VC++多文档项目同时显示多个文本文件

VC6新建一个多文档工程&#xff1b;工程名为txt&#xff1b;生成的类如下&#xff1b;与单文档项目相比多了一个ChildFrm&#xff1b; 在类向导为Doc类添加OnOpenDocument函数&#xff1b; 此时生成的OnOpenDocument()函数如下&#xff1b; BOOL CTxtDoc::OnOpenDocument(LPCT…

Python基础 —— 条件语句

考虑了好久&#xff0c;不知道是先写条件循环&#xff0c;还是先写数据类型,因为如果先写条件循环的话&#xff0c;要涉及到数据类型的内容&#xff1b;先写数据类型的话&#xff0c;又要设计到条件循环的内容…纠结一番后&#xff0c;决定还是先说条件循环&#xff0c;再在数据…

chatgpt赋能python:用Python抓取数据:提高SEO的关键

用Python抓取数据&#xff1a;提高SEO的关键 在数字化时代&#xff0c;数据已经变成了最宝贵的财富之一。然而&#xff0c;对于企业和网站管理者来说&#xff0c;数据仅仅是有价值的当它被收集和转化成行动中存在的信息。这时&#xff0c;Python成为了一个有用的工具&#xff…

基于VORS、CCDM模型、GeoDetector、GWR模型集成技术在城镇化与生态系统健康空间关系分析及影响效应中的应用

城市群是一国经济发展水平的象征&#xff0c;也是一国经济发展到一定阶段的标志&#xff0c;我国城市群建设体量不断增加&#xff0c;将成为全球经济的核心&#xff0c;中国城市群的建设逐步引领全球进入到了21世纪的中国新时代。然而&#xff0c;高速的城镇化发展&#xff0c;…

leetcode题集训 sql

目录 背景步骤175组合两个表&#xff08;多表联查&#xff09;176 177 第n高的薪水&#xff08;Distinct关键字 排序&#xff09;178分数排名 &#xff08;排序 order over关键字&#xff09;179 连续出现的数字 &#xff08;模拟多张表联查&#xff09;181. 超过经理收入的员工…

学号编码:TooY0ung的学院(结构体)

根据66十二位编码规则&#xff0c;用城市代码和出生年编制学号。 【本笔记适合初通算法的 coder 翻阅】 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免费“圣经”教程《 python 完全自学教程》&#xff0c;不仅仅是基础…

Web-文件上传漏洞总结

目录 1、常规前端绕过 2、修改文件类型 3、使用 .user.ini 或 .htaccess&#xff08;可能还存在大小写绕过&#xff09; 4、使用字典爆破可行后缀 5、结合文件包含漏洞使用图片马 6、条件竞争 1、常规前端绕过 如下图&#xff0c;在前端存在限制&#xff0c;只能上传图片…

springboot校园点餐小程序

校园点餐系统 springboot校园点餐系统小程序 java校园点餐小程序 技术&#xff1a; 基于springbootvue小程序校园点餐系统的设计与实现 运行环境&#xff1a; JAVA版本&#xff1a;JDK1.8 IDE类型&#xff1a;IDEA、Eclipse都可运行 数据库类型&#xff1a;MySql&#xff08;…

GPT模型训练实践(2)-Transformer模型工作机制

Transformer 的结构如下&#xff0c;主要由编码器-解码器组成&#xff0c;因为其不需要大量标注数据训练和天然支持并行计算的接口&#xff0c;正在全面取代CNN和RNN&#xff1a; 扩展阅读&#xff1a;What Is a Transformer Model? ​ ​ 其中 编码器中包含自注意力层和前馈…

HCIA回顾笔记整理

OSI 7层参考模式 开放式系统互联参考模型 应用层 抽象语言--> 编码 表示层 编码-->二进制 会话层 提供应用程序地址 -- 无标准 上三层&#xff0c;应用程序加工数据的部分 下四层&#xff0c;数据流层 负责数据传输 传输层 数据分段&#xff08;…

Hive基础知识

1.Hive简介 Hive是由Facebook开源用于解决海量结构化日志的数据统计工具。Hive是基于Hadoop的一个数据仓库工具&#xff0c;可以将结构化的数据文件映射为一张表&#xff0c;并提供类SQL查询功能。 2.Hive本质 Hive的本质是将HQL转化成MapReduce程序。 Hive处理的数据存储在H…