【Linux】C文件系统详解(三)——如何理解缓冲区以及自主封装一个文件接口

文章目录

  • 如何理解缓冲区
    • 现象
    • 概念:文件缓冲区
    • 为什么要有缓冲区
    • 缓冲区在哪里
  • 自己封装一个简单的文件接口
    • 自主封装
      • 目标
    • 代码
    • 关于缓冲区
      • 强制刷新内核
  • 关于字符串格式化函数
    • printf和scanf函数

如何理解缓冲区

以前写过一个进度条, 有一个输出缓冲区->这个缓冲区在哪里,为什么要存在
struct file [缓冲区]中的缓冲区与上面这个缓冲区有关系吗

1.先看现象->提出问题
2.提出文件缓冲区
3.解释问题

现象

int main()
{//C库fprintf(stdout,"hello fprintf\n");//系统调用const char* msg = "hello write\n";write(1,msg,strlen(msg));//这里不用加上\0fork();return 0
}

这样可以打印出来

hello fprintf
hello write

因为此时都是向显示器打印,是采用行缓冲,所以直接就刷新出来的(见下图中的解释)
但是如果我们重定向:./myfile > log.txt
结果不一样了:

hello write
hello fprintf
hello fprintf

但是如果不加fork();就不会产生这样的结果.
因为此时是普通文件,采用的刷新策略是全缓冲
所以真正的调用顺序应该是:在fork之前,write就直接打印进文件了,但是fwrite只是写在缓冲区中.在fork之后,fwrite的缓冲区中的文件变成了两份(写时拷贝),由此,会出现打印两次的现象.(下图中有解释)

概念:文件缓冲区

![[文件系统 2023-11-16 16.12.27.excalidraw|800]]

为什么要有缓冲区

可以节省调用者的时间:系统调用也是要花费大量时间的
进程可以继续做自己的事情,最后统一刷新

缓冲区在哪里

在你进行fopen打开文件的时候,会得到一个FILE结构体,缓冲区就在该结构体中
而调用write时,是系统调用,没有缓冲区,会直接刷新出来

自己封装一个简单的文件接口

自主封装

目标

用最简单的方式,呈现出对FILE的理解
特点:实现的是一个demo版本,重在呈现原理

代码

makefile

myfile:main.c myfile.cgcc -o $@ $^ 
.PHONY:clean
clean:rm -f myfile

myfile.h

#pragma once#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 0x4typedef struct MY_FILE
{int fd;char outputbuffer[NUM];int flags; // 刷新方式int current;
} MY_FILE;MY_FILE *my_fopen(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);

myfile.c

#include "myfile.h"MY_FILE *my_fopen(const char *path, const char *mode)
{int flags = 0;if (strcmp(mode, "r") == 0)flags |= O_RDONLY;else if (strcmp(mode, "w") == 0)flags |= (O_WRONLY | O_CREAT | O_TRUNC);else if (strcmp(mode, "a") == 0)flags |= (O_WRONLY | O_CREAT | O_APPEND);mode_t m = 0666;int fd = 0;if (flags & O_CREAT)fd = open(path, flags, m);elsefd = open(path, flags);if (fd < 0){perror("open error");return NULL;}MY_FILE *mf = (MY_FILE *)malloc(sizeof(MY_FILE));if (mf == NULL){close(fd);return NULL;}mf->fd = fd;mf->flags = 0;mf->current = 0;mf->flags |= BUFF_LINE;// mf->outputbuffer[0] = 0;//初始化缓冲区memset(mf->outputbuffer, '\0', sizeof(mf->outputbuffer));return mf;
}int my_fflush(MY_FILE *fp)
{assert(fp);// 将用户缓冲区中的数据,通过系统调用接口,冲刷给OSwrite(fp->fd, fp->outputbuffer, fp->current);fp->current = 0;fsync(fp->fd);return 0;
}size_t my_fwrite(const void *ptr, size_t size, size_t nmemb, MY_FILE *stream)
{// 1. 缓冲区如果已经满了,就直接写入assert(stream);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. 开始计划刷新, 他们高效体现在哪里 -- TODO// 不发生刷新的本质,不进行写入,就是不进行IO,不进行调用系统调用,所以my_fwrite函数调用会非常快,数据会暂时保存在缓冲区中// 可以在缓冲区中积压多份数据,统一进行刷新写入,本质:就是一次IO可以IO更多的数据,提高IO效率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);}return writen;
}int my_fclose(MY_FILE *fp)
{assert(fp);// 1.关闭文件的时候,C要帮助我们进行冲刷缓冲区if (fp->current > 0){my_fflush(fp);}// 2.关闭文件close(fp->fd);// 3.释放堆空间free(fp);// 4.指针置为NULLfp = NULL;return 0;
}

关于缓冲区

1.历史上我们所谈的缓冲区指的是:用户级缓冲区,语言提供
2.用户层+内核->强制刷新内核

![[文件系统 2023-11-17 10.15.39.excalidraw|900]]

强制刷新内核

fsync(fp->fd);

关于字符串格式化函数

printf和scanf函数

int my_printf(const char* format,...)
{//1.先获取对应的变量a//2.定义缓冲区,对a转成字符串//2.1 fwrite(stdout,str);//3.将字串拷贝的stdout->buffer即可//4.结合刷新策略显示即可
}

![[Pasted image 20230325111337.png]]
![[Pasted image 20230325111513.png]]
完结.

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

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

相关文章

hyperledger fabric2.4测试网络添加组织数量

!!!修改内容比较繁琐,预期未来提供模板修改 修改初始配置文件,初始添加3个组织 organizations文件夹 /cryptogen文件夹下创建文件crypto-config-org3.yaml,内容如下: PeerOrgs:# ---------------------------------------------------------------------------# Org3# ----…

酷柚易汛ERP - 序列号盘点操作指南

1、应用场景 将系统中开启序列号的商品数量与与实际存放的数量进行对比。 2、主要操作 2.1 录入序列号 打开【盘点】-【序列号盘点】&#xff0c;新增序列号盘点单&#xff0c;点击【SN】按钮&#xff0c;在弹框中输入序列号。 支持扫描枪录入序列号支持复制粘贴序列号录入…

Java(二)(String的常见方法,ArrayList的常见方法)

String 创建string对象 package Helloworld;public class dome1 {public static void main(String[] args) {// 1.直接双引号得到字符串对象,封装字符串对象String name "lihao";System.out.println(name);// 2. new String 创建字符串对象,并调用构造器初始化字符…

Vue第1天:特性概览

文章目录 Vue.js 简介 Vue的特性 如何使用Vue 安装Vue 通过CDN引入 使用npm 创建Vue实例 结语 Vue.js 简介 Vue.js&#xff08;通常简称为Vue&#xff09;是一款流行的JavaScript框架&#xff0c;专注于构建用户界面。它的设计灵感来自于现代的JavaScript框架&#xf…

用Java写一个贪吃蛇游戏

目录 创建窗体 创建游戏棋盘 创建蛇的节点 创建蛇身 让蛇移动 添加键盘监听 添加边界处理 把食物绘制到棋盘上 吃食物 创建窗体 创建一个MainFrame类继承JFrame&#xff0c;表示主窗体。 public class MainFrame extends JFrame {//创建窗体对象时&#xff0c;初始化…

力扣 字母异位词分组 哈表 集合

&#x1f468;‍&#x1f3eb; 力扣 字母异位词分组 ⭐ 思路 由于互为字母异位词的两个字符串包含的字母相同&#xff0c;因此对两个字符串分别进行排序之后得到的字符串一定是相同的&#xff0c;故可以将排序之后的字符串作为哈希表的键。 &#x1f351; AC code class Solut…

解析Spring Boot中的CommandLineRunner和ApplicationRunner:用法、区别和适用场景详解

在Spring Boot应用程序中&#xff0c;CommandLineRunner和ApplicationRunner是两个重要的接口&#xff0c;它们允许我们在应用程序启动后执行一些初始化任务。本文将介绍CommandLineRunner和ApplicationRunner的区别&#xff0c;并提供代码示例和使用场景&#xff0c;让我们更好…

阿里云配置ssl(Apache)

阿里云申请证书&#xff0c;有个专门的免费的申请方式与普通证书是平级的功能&#xff1b; 访问服务器&#xff0c;判断apache是不是开启ssl功能&#xff0c;如果没有安装就安装它 [rootcentos ~]# rpm -qa | grep mod_ssl //什么没显示说明没装 yum install mod_ssl openssl …

公共字段自动填充-@TableField的fill实现(2)

TheadLocal 客户端发送的每次http请求&#xff0c;在服务端都会分配新的线程。因此登录检查过滤器、controller、元数据对象处理器属于一个线程。 TheadLocal是线程的局部变量&#xff1a; TheadLocal常用方法&#xff1a; 如何在元数据对象处理器中获取当前登录用户的id&…

大数据专业有哪些基础的理论和技术

大数据专业有哪些基础的理论和技术 大数据专业涉及的基础理论和技术很广泛&#xff0c;主要包括以下几个方面&#xff1a; 数据管理与处理理论&#xff1a;大数据处理涉及到海量数据的管理、存储和处理&#xff0c;因此数据库理论、数据结构与算法、数据压缩与索引等知识是非常…

Spring Boot 项目部署方案!打包 + Shell 脚本部署详解

文章目录 概要一 、profiles指定不同环境的配置二、maven-assembly-plugin打发布压缩包三、 分享shenniu_publish.sh程序启动工具四、linux上使用shenniu_publish.sh启动程序 概要 本篇和大家分享的是springboot打包并结合shell脚本命令部署&#xff0c;重点在分享一个shell程…

【MATLAB源码-第83期】基于matlab的MIMO中V-BALST结构ZF和MMSE检测算法性能误码率对比。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 在多输入多输出&#xff08;MIMO&#xff09;通信系统中&#xff0c;V-BLAST&#xff08;垂直波束形成层间空间时间编码技术&#xff09;是一种流行的技术&#xff0c;用于提高无线通信的数据传输速率和容量。它通过在不同的…

如何将 Docsify 项目部署到 CentOS 系统的 Nginx 中

文章目录 第一步&#xff1a;准备 CentOS 服务器第二步&#xff1a;安装 Node.js 和 Docsify第三步&#xff1a;初始化 Docsify 项目第四步&#xff1a;本地预览 Docsify 项目第五步&#xff1a;配置 Nginx 服务器第六步&#xff1a;重启 Nginx 服务器拓展&#xff1a;使用 HTT…

k8s-部署Redis-cluster(TLS)

helm pull bitnami/redis-cluster v8.3.8拉取源码生成证书 git clone https://github.com/redis/redis.git #文档 https://redis.io/docs/management/security/encryption/#getting-started生成你的TLS证书用官网的工具生成 1 Run ./utils/gen-test-certs.sh 生成根CA和服务…

Python 如何实现备忘录设计模式?什么是备忘录设计模式?Python 备忘录设计模式示例代码

什么是备忘录&#xff08;Memento&#xff09;设计模式&#xff1f; 备忘录&#xff08;Memento&#xff09;设计模式是一种行为型设计模式&#xff0c;用于捕获一个对象的内部状态&#xff0c;并在对象之外保存这个状态&#xff0c;以便在需要时恢复对象到先前的状态。这种模…

JAVA for 循环训练 Pattern

import java.util.Scanner;public class Pattern {public static void main(String[] args) {int[] arr {0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0};Scanner in new Scanner(System.in);System.out.print("请输入n:");int n in.nextInt();in.close();for …

【小沐学GIS】电子海图OpenCPN源代码编译和运行(VS2017 + Win10)

1、简介 免费的开源海图仪和船用GPS导航软件 https://opencpn.org/ 1.1 OpenCPN概述 OpenCPN是一款自由软件&#xff08;GPLv2&#xff09;&#xff0c;用于创建简洁的海图绘图仪和导航软件&#xff0c;可以在航行过程中使用或者作为计划工具。OpenCPN提供大量免费海图下载&a…

「项目阅读系列」go-gin-example star 6.5k!(1)

文章目录 准备工作适宜人群项目信息 项目结构代码阅读主要模块代码主函数模块router 路由模块auth 授权模块数据库 修改文章请求分析其他依赖 总结 准备工作 适宜人群 初学 go 语法&#xff0c;希望了解 go 项目的构建过程和方式。 项目信息 go-gin-example 项目是使用 gin…

037、目标检测-SSD实现

之——简单实现 目录 之——简单实现 杂谈 正文 1.类别预测层 2.边界框预测 3.多尺度输出联结做预测&#xff08;提高预测效率&#xff09; 4.多尺度实现 5.基本网络块 6.完整模型 杂谈 原理查看&#xff1a;037、目标检测-算法速览-CSDN博客 正文 1.类别预测层 类别…

Python学习(一)基础语法

文章目录 1. 入门1.1 解释器的作用1.2 下载1.3 基础语法输入输出语法与引号注释&#xff1a;变量&#xff1a; 数据类型与四则运算数据类型四则运算数据类型的查看type()数据类型的转换int()、int()、float() 流程控制格式化输出循环与遍历逻辑运算符list遍历字典dict遍历 跳出…