Linux文件系列: 深入理解缓冲区和C标准库的简单模拟实现

Linux文件系列: 深入理解缓冲区和C标准库的简易模拟实现

  • 一.缓冲区的概念和作用
  • 二.一个样例
  • 三.理解样例
    • 1.样例解释
    • 2.什么是刷新?
  • 四.简易模拟实现C标准库
    • 1.我们要实现的大致框架
    • 2.mylib.h的实现
      • 1.文件结构体的定义
      • 2.myfopen等等函数的声明
      • 3.完整mylib.h代码
    • 3.myfopen函数的实现
    • 4.myfwrite函数的实现
    • 5.myfflush函数的实现
    • 6.myfclose函数的实现
    • 7.演示
    • 8.完整代码
      • 1.mylib.h
      • 2.mylib.c
      • 3.main.c

一.缓冲区的概念和作用

在这里插入图片描述

二.一个样例

在这里插入图片描述
在这里插入图片描述

三.理解样例

1.样例解释

在这里插入图片描述
在这里插入图片描述

2.什么是刷新?

在这里插入图片描述

四.简易模拟实现C标准库

至此,我们理解了缓冲区的概念和作用,下面我们来简易模拟实现一下C标准库

1.我们要实现的大致框架

我们要实现的是:
在这里插入图片描述

2.mylib.h的实现

1.文件结构体的定义

1.首先要有一个文件结构体:

结构体当中
1.要封装文件描述符fd    设置成员变量fileno
2.用户级缓冲区buffer     大小宏定义为SIZE 4096
3.该文件所对应缓冲区的刷新策略 flag

刷新策略分别宏定义为

#define FLUSH_NONE 1        不刷新
#define FLUSH_LINE (1<<1)   行刷新
#define FLUSH_ALL (1<<2)    全刷新
4.缓冲区中有效数据的个数  end
#define SIZE 4096

#define FLUSH_NONE 1
#define FLUSH_LINE (1<<1)
#define FLUSH_ALL (1<<2)

typedef struct my_file
{
    int fileno;
    char buffer[SIZE];
    int end;//缓冲区中有效数据的个数(也就是最后一个有效数据的下一个位置)
    int flag;//缓冲区的刷新策略
}my_file;

2.myfopen等等函数的声明

在这里插入图片描述

1.myfopen:以mode的方式打开path这个文件

path:文件路径+文件名
mode:打开文件的方式
“r”:只读
“w”:覆盖写
“a”:追加写

2.myfwrite当中:把s字符串中前num个数据写入stream文件中

stream:要往哪个文件当中写入数据,stream是对应文件的结构体指针
s:有数据的字符串
num:要写入的数据个数

3.myfflush:刷新文件缓冲区
4.myfclose:关闭该文件

3.完整mylib.h代码

DFL_MODE : 打开文件的默认权限
在这里插入图片描述

3.myfopen函数的实现

在这里插入图片描述

my_file* myfopen(const char* path,const char* mode)
{
    int fd=0;
    int flag=0;
    if(strcmp(mode,"r")==0)
    {
        flag |= O_RDONLY;
    }
    else if(strcmp(mode,"w")==0)
    {
        flag |= (O_WRONLY | O_CREAT | O_TRUNC);
    }
    else if(strcmp(mode,"a")==0)
    {
        flag |= (O_WRONLY | O_CREAT | O_APPEND);
    }
    if(flag & O_CREAT)
    {
        fd=open(path,flag,DFL_MODE);
    }
    else
    {
        fd=open(path,flag);
    }
    //打开文件失败,设置errno错误码并返回NULL
    if(fd==-1)
    {
        errno=2;
        return NULL;
    }
    //创建文件,设置fp的相应属性
    my_file* fp=(my_file*)malloc(sizeof(my_file));
    if(fp==NULL)
    {
        errno=3;
        return NULL;
    }
    fp->fileno=fd;
    fp->flag=FLUSH_LINE;
    fp->end=0;
    return fp;
}

4.myfwrite函数的实现

在这里插入图片描述

//把s中的数据写入stream中
int myfwrite(const char* s,int num,my_file* stream)
{
    //保存旧的缓冲区的大小
    int pos=stream->end;
    //1.先写入用户级缓冲区
    memcpy(stream->buffer+pos,s,num);
    stream->end += num;//更新缓冲区大小
    
    //刷新策略:按行刷新
    if(stream->flag & FLUSH_LINE)
    {
        //2.判断是否需要刷新缓冲区(判断是否有'\n')
        int flushit=0;
        while(pos < stream->end)
        {
            if((stream->buffer[pos])=='\n')
            {
                flushit=1;
                break;
            }
            pos++;
        }

        if(flushit == 1)
        {
            //3.刷新缓冲区:[0,pos]数据
            write(stream->fileno,stream->buffer,pos+1);
            //4.更新缓冲区 把[pos+1,count)的数据移动到[0,count-pos-2]当中
            //一共移动count-pos-1个数据
            //先求出要移动的最后一个数据的下标
            int count=stream->end;
            memmove(stream->buffer,stream->buffer+pos+1,count-pos-1);
            stream->buffer[count-pos-1]='\0';
            stream->end=count-pos-1;
        }
    }
    return num;
}

5.myfflush函数的实现

在这里插入图片描述

int myfflush(my_file* fp)
{
    if(fp->end > 0)
    {
        write(fp->fileno,fp->buffer,fp->end);
        fp->end=0;
    }
    return 0;
}

6.myfclose函数的实现

在这里插入图片描述

int myfclose(my_file* fp)
{
    myfflush(fp);
    return close(fp->fileno);
}

7.演示

下面我们测试一下
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
跟我们所预想的一样

8.完整代码

1.mylib.h

#pragma once
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>

#define SIZE 4096
#define DFL_MODE 0666

#define FLUSH_NONE 1
#define FLUSH_LINE (1<<1)
#define FLUSH_ALL (1<<2)

typedef struct my_file
{
    int fileno;
    char buffer[SIZE];
    int end;//缓冲区中有效数据的个数(也就是最后一个有效数据的下一个位置)
    int flag;//缓冲区的刷新策略
}my_file;

my_file* myfopen(const char* path,const char* mode);

int myfwrite(const char* s,int num,my_file* stream);

int myfflush(my_file* fp);

int myfclose(my_file* fp);

2.mylib.c

#include "mylib.h"

my_file* myfopen(const char* path,const char* mode)
{
    int fd=0;
    int flag=0;
    if(strcmp(mode,"r")==0)
    {
        flag |= O_RDONLY;
    }
    else if(strcmp(mode,"w")==0)
    {
        flag |= (O_WRONLY | O_CREAT | O_TRUNC);
    }
    else if(strcmp(mode,"a")==0)
    {
        flag |= (O_WRONLY | O_CREAT | O_APPEND);
    }
    if(flag & O_CREAT)
    {
        fd=open(path,flag,DFL_MODE);
    }
    else
    {
        fd=open(path,flag);
    }
    //打开文件失败,设置errno错误码并返回NULL
    if(fd==-1)
    {
        errno=2;
        return NULL;
    }
    //创建文件,设置fp的相应属性
    my_file* fp=(my_file*)malloc(sizeof(my_file));
    if(fp==NULL)
    {
        errno=3;
        return NULL;
    }
    fp->fileno=fd;
    fp->flag=FLUSH_LINE;
    fp->end=0;
    return fp;
}


//把s中的数据写入stream中
int myfwrite(const char* s,int num,my_file* stream)
{
    //保存旧的缓冲区的大小
    int pos=stream->end;
    //1.先写入用户级缓冲区
    memcpy(stream->buffer+pos,s,num);
    stream->end += num;//更新缓冲区大小
    
    //刷新策略:按行刷新
    if(stream->flag & FLUSH_LINE)
    {
        //2.判断是否需要刷新缓冲区(判断是否有'\n')
        int flushit=0;
        while(pos < stream->end)
        {
            if((stream->buffer[pos])=='\n')
            {
                flushit=1;
                break;
            }
            pos++;
        }

        if(flushit == 1)
        {
            //3.刷新缓冲区:[0,pos]数据
            write(stream->fileno,stream->buffer,pos+1);
            //4.更新缓冲区 把[pos+1,count)的数据移动到[0,count-pos-2]当中
            //一共移动count-pos-1个数据
            //先求出要移动的最后一个数据的下标
            int count=stream->end;
            memmove(stream->buffer,stream->buffer+pos+1,count-pos-1);
            stream->buffer[count-pos-1]='\0';
            stream->end=count-pos-1;
        }
    }
    return num;
}

int myfflush(my_file* fp)
{
    if(fp->end > 0)
    {
        write(fp->fileno,fp->buffer,fp->end);
        fp->end=0;
    }
    return 0;
}

int myfclose(my_file* fp)
{
    myfflush(fp);
    return close(fp->fileno);
}

3.main.c

#include "mylib.h"
int main()
{
    my_file* fp=myfopen("./log.txt","a");
    if(fp==NULL)
    {
        perror("myfopen fail");
        return 1;
    }
    int cnt=10;
    
    const char* message="abc\ndef";
    while(cnt--)
    {
        mywrite(message,strlen(message),fp);
        sleep(1);
    }
    myfclose(fp);
    return 0;
}

以上就是Linux文件系列: 深入理解缓冲区和C标准库的简单模拟实现的全部内容,希望能对大家有所帮助!

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

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

相关文章

备战蓝桥杯Day25 - 二叉搜索树

一、基本概念 二叉搜索树&#xff08;Binary Search Tree&#xff09;&#xff0c;又称为二叉查找树或二叉排序树&#xff0c;是一种具有特定性质的二叉树。 定义&#xff1a;二叉搜索树可以是一棵空树&#xff0c;也可以是具有以下特性的非空二叉树&#xff1a; 若其左子树不…

MAC OS 14.2.1 ASP.NET Core 调试遇到的端口占用的问题

一、问题描述 在调试 ASP.NET Core 项目时&#xff0c;遇到一个很奇怪的问题&#xff0c;不管项目是否已经运行&#xff0c;使用 Postman 测试接口时&#xff0c;都返回 403 Forbidden。重启电脑&#xff0c;刚开始还好好的&#xff0c;过一会儿就返回 403 Forbidden。 二、问…

AOP切面编程,以及自定义注解实现切面

AOP切面编程 通知类型表达式重用表达式切面优先级使用注解开发&#xff0c;加上注解实现某些功能 简介 动态代理分为JDK动态代理和cglib动态代理当目标类有接口的情况使用JDK动态代理和cglib动态代理&#xff0c;没有接口时只能使用cglib动态代理JDK动态代理动态生成的代理类…

2024蓝桥杯每日一题(双指针)

一、第一题&#xff1a;牛的学术圈 解题思路&#xff1a;双指针贪心 仔细思考可以知道&#xff0c;写一篇综述最多在原来的H指数的基础上1&#xff0c;所以基本方法可以是先求出原始的H指数&#xff0c;然后分类讨论怎么样提升H指数。 【Python程序代码】 n,l map(int,…

一篇文章搞定数字电桥

大家好&#xff0c;我是砖一。 最近做项目过程中&#xff0c;有一个应届生问我怎么测电容和电感&#xff0c;我推荐他使用数字电桥&#xff08;也叫LCR表&#xff09;&#xff0c;他不会用&#xff0c;今天我写了一篇文章供小白们参考一下&#xff0c;包学会~ 一&#xff0c;…

WebRTC简介及实战应用 — 从0到1实现实时音视频聊天等功能

一、WebRTC简介 WebRTC 是由一家名为 Gobal IP Solutions,简称 GIPS 的瑞典公司开发的。Google 在 2011 年收购了 GIPS,并将其源代码开源。然后又与 IETF 和 W3C 的相关标准机构合作,以确保行业达成共识。其中: Web Real-Time Communications (WEBRTC) W3C 组织:定义浏览…

【npm】node包管理工具npm的介绍和基础使用

简言 npm 是 Node.js 的 包管理器&#xff08;Package Manager&#xff09;&#xff0c;它是专门用于管理 Node.js 项目中第三方库的工具。 本文介绍下npm和其使用方法。 npm介绍 npm 是世界上最大的软件注册中心。各大洲的开源开发者都使用 npm 共享和借用软件包&#xff…

开源组件安全风险及应对

在软件开发的过程中&#xff0c;为了提升开发效率、软件质量和稳定性&#xff0c;并降低开发成本&#xff0c;使用开源组件是开发人员的不二选择&#xff08;实际上&#xff0c;所有软件开发技术的演进都是为了能够更短时间、更低成本地构建软件&#xff09;。这里的开源组件指…

Spring事件发布监听器ApplicationListener原理- 观察者模式

据说监听器模式也是mq实现的原理, 不过mq我还没来得及深入学习, 先用spring来理解一下吧 Spring事件发布监听器ApplicationListener原理- 观察者模式 什么是观察者模式一个Demo深入认识一下观察者模式Spring中的事件发布监听ps 什么是观察者模式 大家都听过一个故事叫做烽火戏…

Git学习笔记(流程图+示例)

概念 图中左侧为工作区&#xff0c;右侧为版本库。Git 的版本库里存了很多东西&#xff0c;其中最重要的就是暂存区。 • 在创建 Git 版本库时&#xff0c;Git 会为我们自动创建一个唯一的 master 分支&#xff0c;以及指向 master 的一个指 针叫 HEAD。&#xff08;分支和HEAD…

设计模式-结构型模式-代理模式

代理模式&#xff08;Proxy&#xff09;&#xff0c;为其他对象提供一种代理以控制对这个对象的访问。[DP] // 定义接口 interface Subject {void request(); }// 真实主题对象 class RealSubject implements Subject {Overridepublic void request() {System.out.println(&quo…

redis使用笔记

redis使用笔记 1、Redis简介1.1 含义1.2 功能1.3 特点 2. 常用的数据结构2.1 HASH 3 redis接口定义3.1 redisReply3.2 redisContext3.3 redisCommand 4 实践操作4.1 遇到问题4.1.1 Get哈希的时候返回error4.1.2 长度一直为0&#xff0c;str没法打印&#xff08;未解决&#xff…

享受当下,2024,一顺百顺!

永远都不要提前焦虑&#xff0c; 也不要一直烦恼&#xff0c; 更不要被当前的情绪耗尽&#xff0c; 那样会对自己的身体造成严重的伤害&#xff0c; 自己只需要好好享受当下就行了。 车到山前必有路&#xff0c;关关难过关关过&#xff01; 不要执着于过去&#xff0c;也不要过…

10-ARM gicv3/gicv4的总结-基础篇

目录 1、gic的版本2、GICv3/gicv4的模型图3、gic中断号的划分4、GIC连接方式5、gic的状态6、gic框架7、gic Configuring推荐 本文转自 周贺贺&#xff0c;baron&#xff0c;代码改变世界ctw&#xff0c;Arm精选&#xff0c; armv8/armv9&#xff0c;trustzone/tee&#xff0c;s…

LeetCode59:螺旋矩阵Ⅱ

题目描述 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;[[1,2,3],[8,9,4],[7,6,5]] 代码 class Solution { public:vector…

AWS 入门实践-远程访问AWS EC2 Linux虚拟机

远程访问AWS EC2 Linux虚拟机是AWS云计算服务中的一个基本且重要的技能。本指南旨在为初学者提供一系列步骤&#xff0c;以便成功地设置并远程访问他们的EC2 Linux实例。包括如何上传下载文件、如何ssh远程登录EC2虚拟机。 一、创建一个AWS EC2 Linux 虚拟机 创建一个Amazon…

GSEA -- 学习记录

文章目录 brief统计学原理部分其他注意事项转录组部分单细胞部分 brief 上一篇学习记录写了ORA&#xff0c;其中ORA方法只关心差异表达基因而不关心其上调、下调的方向&#xff0c;也许同一条通路里既有显著高表达的基因&#xff0c;也有显著低表达的基因&#xff0c;因此最后…

iMazing3 2024详细解析数据备份与恢复备份

iMazing 3的备份功能支持增量备份&#xff08;类似苹果电脑里的Time Machine功能&#xff09;&#xff0c;意思是第一次把移动设备的数据全部备份下来&#xff0c;之后的备份就只针对数据有变化的那部分&#xff0c;这样可以节省大量的时间和存储空间&#xff0c;不会让使用者为…

LeetCode刷题日志-17.电话号码的字母组合

纯暴力解法&#xff0c;digits有多长&#xff0c;就循环多少次进行字母组合 class Solution {public List<String> letterCombinations(String digits) {List<String> reslut new ArrayList<>();if(digits.equals(""))return reslut;Map<Inte…

ubuntu 23.04 安装 中文输入法

1、安装 fcitx sudo apt install fcitxfcitx 安装好后&#xff0c;可以使用 fcitx-configtool 命令进行配置&#xff0c;其界面如下所示。在这里可以配置不同输入法的切换快捷键&#xff0c;默认输入法等。刚安装系统后&#xff0c;这里只有一个输入法&#xff0c;所以接下来要…