【Linux笔记】缓冲区的概念到标准库的模拟实现

news2024/11/28 21:52:18

一、缓冲区

“缓冲区”这个概念相信大家或多或少都听说过,大家其实在C语言阶段就已经接触到“缓冲区”这个东西,但是相信大家在C语言阶段并没有真正弄懂缓冲区到底是个什么东西,也相信大家在C语言阶段也因为缓冲区的问题写出过各种bug。

其实这也不奇怪,因为“缓冲区”这个概念其实已经不是语言层面的东西了,而是系统层面的东西。所以今天我们就要来好好的认识一下这个让我们即熟悉又陌生的“缓冲区”。

1.1、什么是缓冲区?

“缓冲区”我们简单的理解就是一个数据暂存库,当我们要将数据从一个地方传送到另一个地方的时,可以先将数据暂存到这个暂存库中,等时机到了再将数据传送到目标地点。

这就好比我们生活中的快递站,当我们要把一个东西送给另一个人时,就可以先将数据放到快递站,快递站到了时间就会发货,以送往目的地。

而我们的操作系统会为每一个被打开的文件创建一个缓冲区。我们知道,我们创建的文件最终都是要被存储在磁盘中的,而我们要对一个文件进行增删查改操作时,一定要先将文件加载到内存,这时候操作系统为了管理被打开的文件,就要为被打开的文件创建一个struct file结构体对象,这个结构体对象中有很多关于管理这个文件的属性,包括文件的大小、文件的创建时间、文件名、文件的操作方法集、文件的权限、等等……

而其中就有一个缓冲区,当我们要要向文件中写入数据时,就可以先将数据写入到这个缓冲区中,等到了“一定时间”后,操作系统就会将我们写入到缓冲区中的数据统一写入到磁盘中。

而这个struct file是一个内存中的结构体,所以缓冲区的本质其实就是一个内存块。

结构大致如下图所示:

1.2、为什么要有缓冲区

那为什么要有有这么一个缓冲区夹在内存和磁盘中间呢?为什么我们不能直接将数据写入磁盘呢?

这是因为冯诺依曼体系结构:cpu不直接跟外设打交道,cpu是通过内存和外设打交道的,磁盘也属于外设,所以cpu不直接跟磁盘打交道。因为相对于cpu来说磁盘等外设的运行速度实在是太慢了,与cpu的速度相差太多,如果cpu直接跟外设打交道,cpu不然就要等待外设,这就会大大的降低cpu执行的效率。

所以我们我们每次向文件中写入一点东西就直接写入磁盘中,那必定就要求cpu每次都要执行拷贝任务,向磁盘中写入,这样速度就慢了。

所以我们才需要一个缓冲区,先将要写入磁盘的数据暂存起来,等到某一时间后再统一写入到磁盘中,这其实是减少了cpu与磁盘交互的次数从而提高效率。

而将数据从缓冲区中写入到磁盘中称之为缓冲区的刷新,缓冲区的刷新一般有一下5种形式:

1、无缓冲(立即刷新)

2、行缓冲(行刷新)

3、全缓冲(缓冲区满了,再刷新)

4、强制刷新

5、进程退出,自动刷新

无缓冲我这里找不到对应的场景。

行缓冲就是缓冲一行,具体到语法层面就是如果我们在打印的字符串中添加了‘\n’,那就算一行了,那就会将这一行刷新:

比如上面的代码,如果我们加上两个'\n',运行的时候就会全都刷新出来:

而如果我们只在hello后面加上‘\n’,那执行的时候,就会只打印出hello:

那后面的内容呢?答案是:存储在缓冲区之中。

全缓冲就是缓冲区满了,必须刷新了。

强制刷新一般是我们使用一些系统调用来强制刷新缓冲区,例如我们以前是用过的fflush,例如下面这个例子:

上面的代码,如果我们直接运行,一开始是看不到打印的信息的:

只有等时间到了,进程退出的时候自动刷新才能看得见:

如果我们想要让信息立即刷新出来就可以调用fflush:

1.3、语言缓冲区和内核缓冲区

我们上面所谈到的“缓冲区”其实是“内核级缓冲区”,但在我们的语言层面其实还有一个“语言级缓冲区”。

想要讲清楚内核级缓冲区和语言级缓冲区,我们得要从一个奇怪的例子入手:

执行上面的代码,如果我们正常执行,它就是在显示器上按顺序打印出我们所写的内容:

这个没什么问题,但如果我们在执行的时候加上个重定向操作,其结果就有一丢丢奇怪了:

我们会发现代码里面写在最后的write的内容竟然输出到了文件的最开头。

而如果我们再在代码的最后面加上一个fork的话,又会发生什么呢?

如果正常运行的话,那它和不带fork的代码的执行结果是一样的:

但如果我们在执行的时候加上一个重定向,那结果就会变得非常奇怪了:

我们会发现一个,这里出了系统调用的打印会打印到文件的最前面之外,C库函数的打印都打了两次!

想要解释这样的现象,我们就得要慢慢分析了:

1、首先有一个告知的结论是:我们直接向显示器上打印的时候,显示器文件的默认刷新方式是行刷新,所以我们前面的例子中才会出现只要有一个'\n'就会刷新一次的现象。而我们重定向就是向磁盘文件中写入的时候,磁盘文件的刷新方式是是全缓冲,也就是默认等缓冲区写满再刷新,但默认全缓冲不代表一定得等缓冲区写满才能更新,也有可能强制刷新或者是进程退出了自动刷新。

2、我们上面所写的打印信息并不足以将缓冲区写满,所以在fork执行后,数据依旧在缓冲区里面,没有被刷新。

3、而我们在fork创建子进程之后,子进程会进程父进程的文件描述符表和数据:

如上图,所以两个进程的1号文件描述符都指向了log.txt的struct flie,也就是说两个进程都会重定向到log.txt中。

而我们在代码中所写的各种C语言接口的打印,其实并没有直接将数据写入到struct file中的内核文件缓冲区中,而是写入到了C语言给我们提供的一个语言缓冲区中:

所以我们的数据其实是存在两份的,最后当父子进程无论哪个先退出,都会发生“写时拷贝”并刷新缓冲区,将数据写入到内核文件缓冲区中,所以我们代码中C库函数打印的信息,其实是向内核缓冲区中写入了两次的。

而系统调用write的数据只写入了一次,这足以说明,系统调用使用的并非是C语言缓冲区,而是内核缓冲区,它是直接将数据写入到内核缓冲区中的。

二、模拟实现C标准文件操作库

有了上面的这些理论,我们再通过实践来感受一下,缓冲区与C语言标准文件操作库的关系。

我们来模拟实现一个简易的Cstdio库。

我们毕竟不是要实现一个多么完善的stdio文件操作库,只是简单的模拟一下,懂个原理就行了,所以我们就只模拟实现三个接口,分别是fopen、fwrite、fflush、fclose。

首先我们要知道,在C语言中大多的文件操作接口的返回值都是一个FILE*指针,这说明C语言中使用FILE这个结构体来管理被打开的文件的,所以我们还需要在我们自己的stdio头文件中包装一个自己的FILE结构体,而这个结构体中的成员我们今天也不要封装得太复杂,大致像下面这样简单一点就行了:

然后就是声明一下我们自己要实现的一些文件操作接口,模拟库中的即可:

2.1、模拟实现fopen

打开文件的方式第一步一定是先判断文件的打开方式,这里直接用if语句做条件判断即可,判断完了打开方式后,真正打开文件的工作一定是要交给系统调用的:

2.2、模拟实现fflush

然后我们要先来实现fflush,因为无论是close文件或是,向文件中写数据,都需要刷新缓冲区,所以我们要先实现一下fflush。

刷新的本质其实就是将缓冲区内的数据拷贝到内核文件缓冲区中,也就是写入到内核缓冲区中的中,而write正是直接写入到内核缓冲区中的,所以我们需要做的就是调用write将buffer中的数据写入就行了:

 2.3、模拟实现fclose

关闭文件就更简单了,我们只需要在退出前先刷新缓冲区,然后在调用系统调用close关闭文件,最后再讲FILE结构体释放就行了:

2.4、模拟实现fwrite

实现write我们,使用内存拷贝函数,将传过来的数据拷贝到我们的buffer缓冲区中即可,但最后还需要额外判断是否s后面是否包含‘\n’,如果有我们还需要将缓冲区刷新,即行刷新:

测试一下:

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

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

相关文章

【计算机视觉】万字长文详解:卷积神经网络

以下部分文字资料整合于网络,本文仅供自己学习用! 一、计算机视觉概述 如果输入层和隐藏层和之前一样都是采用全连接网络,参数过多会导致过拟合问题,其次这么多的参数存储下来对计算机的内存要求也是很高的 解决这一问题&#x…

2024.2.4

双向链表的头插 头删 尾插 尾删 //头插插入 Doublelink insert_head(Doublelink head,datatype element) {Doublelink screat_Node();s->dataelement;//判断是否有空链表if(NULLhead){heads;}else{s->nexthead;head->priors;heads;}return head; } //头删 Doublelink…

sql相关子查询

1.什么是相关子查询 相关子查询是一个嵌套在外部查询中的查询,它使用了外部查询的某些值。每当外部查询处理一行数据时,相关子查询就会针对那行数据执行一次,因此它的结果可以依赖于外部查询中正在处理的行。 2.为什么要使用相关子…

微信小程序之本地生活案例的实现

学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。各位小伙伴,如果您: 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持,想组团高效学习… 想写博客但无从下手,急需…

图论练习3

内容:过程中视条件改变边权,利用树状数组区间加处理 卯酉东海道 题目链接 题目大意 个点,条有向边,每条边有颜色和费用总共有种颜色若当前颜色与要走的边颜色相同,则花费为若当前颜色与要走的边颜色不同,…

Android学习之路(27) ProGuard,混淆,R8优化

前言 使用java编写的源代码编译后生成了对于的class文件,但是class文件是一个非常标准的文件,市面上很多软件都可以对class文件进行反编译,为了我们app的安全性,就需要使用到Android代码混淆这一功能。 针对 Java 的混淆&#x…

【快速上手QT】01-QWidgetQMainWindow QT中的窗口

总所周知,QT是一个跨平台的C图形用户界面应用程序开发框架。它既可以开发GUI程序,也可用于开发非GUI程序,当然我们用到QT就是要做GUI的,所以我们快速上手QT的第一篇博文就讲QT的界面窗口。 我用的IDE是VS2019,使用QTc…

Leetcode高频题:213打家劫舍II

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 题目描述 你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个…

MySQL知识点总结(三)——事务

MySQL知识点总结(三)——事务 事务事务的四大特性ACID原子性一致性隔离性持久性 脏读、幻读、不可重复读脏读不可重复读幻读 隔离级别读未提交读已提交可重复读串行化 事务的原理InnoDB如何实现事务的ACID事务的两阶段提交redo log与binlog的区别事务两阶…

基于SpringBoot+Vue的在线教育平台设计与实现

目录 项目介绍 技术栈 项目介绍 项目截图 搭建 代码截取 代码获取 项目介绍 近年由于疫情影响,线下教育行业受到较大冲击,因此线上教育培训有较好的发展势头,其中建筑行业考证培训是一个前景良好的发展方向,该行业不仅需要…

权威认可|亚数强势入围FreeBuf《CCSIP 2023中国网络安全产业全景图》10大细分领域

近日,国内安全行业门户FreeBuf旗下FreeBuf咨询正式发布《CCSIP(China Cyber Security Industry Panorama)2023中国网络安全行业全景册(第六版)》。 凭借卓越的技术产品能力、市场影响力及领先的综合实力,亚…

C++泛编程(3)

类模板基础 1.类模板的基本概念2.类模板的分文件编写3.类模板的嵌套 (未完待续...) 在往节内容中,我们详细介绍了函数模板,这节开始我们就来聊一聊类模板。C中,类的细节远比函数多,所以这个专题也会更复杂。…

【Crypto | CTF】BUUCTF 萌萌哒的八戒

天命:这年头连猪都有密码,真是奇葩,怪不得我一点头绪都没有 拿到软件,发现是.zip的压缩包,打不开,改成7z后缀名,打开了 发现是一张图片 也只有下面这行东西是感觉是密码了,又不可能…

[leetcode] 22. 括号生成

文章目录 题目描述解题方法方法一:dfs遍历java代码 方法二:按照卡特兰数的思路递归求出有效括号组合java代码 相似题目 题目描述 数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。 示…

计算机编码:原码、反码、补码的思想、原理和实例(详细版)

​ 目录 收起 一、原码、反码、补码的意义 意义: 三、原码 原码的特点: 原码存在的问题: 四、反码 反码的特点: 存在的问题: 五、补码 六、补码的思想(模&&同余数) 模 && 同余数…

exF2FS: Transaction Support in Log-Structured Filesystem——泛读笔记

FAST 2022 Paper 分布式元数据论文汇总 问题 现代应用程序努力以崩溃一致的方式保护其数据,这通常分布在多个文件抽象之上。在底层文件系统缺乏事务支持的情况下,应用程序使用复杂的协议来确保跨多个文件的事务性更新,产生长序列的写操作和…

小林Coding_操作系统_读书笔记

一、硬件结构 1. CPU是如何执行的 冯诺依曼模型:中央处理器(CPU)、内存、输入设备、输出设备、总线 CPU中:寄存器(程序计数器、通用暂存器、指令暂存器),控制单元(控制CPU工作&am…

自研人工智能小工具-小蜜蜂(国外ChatGpt的平替)

国内有非常多好用的人工智能工具,但均无法完全替代国外ChatGpt。 ChatGPT相较于其他国内工具的优势在于以下几点: 创新的语言生成能力:ChatGPT是由OpenAI开发的先进的自然语言生成模型,它采用了大规模的预训练和精细调整方法。因此…

蓝桥杯刷题day06——平均

1、题目描述 有一个长度为n 的数组(n 是 10 的倍数),每个数ai都是区间 [0,9] 中的整数。 小明发现数组里每种数出现的次数不太平均,而更改第i 个数的代价为bi, 他想更改若干个数的值使得这10 种数出现的次数相等&…

创建TextMeshPro字体文件

相比于Unity的Text组件,TextMesh Pro提供了更强大的文本格式和布局控制,更高级的文本渲染技术,更灵活的文本样式和纹理支持,更好的性能以及更易于使用的优点。但unity自带TextMeshPro字体不支持中文。这里使用普通字体文件生成Tex…