Linux文件系统的缓冲区问题

news2024/10/6 14:37:01

目录

一.什么是缓冲区?

1.1实验案例1:

情况1:运行该程序

情况2:此时我将该程序运行的结果输出重定向到一个文本文件中:

二.为什么要有缓冲区?

于是引出了缓冲区的刷新策略:

三.缓冲区在哪


一.什么是缓冲区?

        说到缓冲区就需要先来看一个程序代码来了解一下什么是缓冲区。

1.1实验案例1:

#include<stdio.h>                    
#include<sys/types.h>                
#include<wait.h>                     
#include<unistd.h>                   
#include<string.h>                                                         
   
 int main(){                                                                  
      //C语言库函数                    
      printf("hello printf\n");        
      fprintf(stdout,"hello fprintf\n");  
      const char* str="hello fputs\n";  
      fputs(str,stdout);               
                                       
      //系统调用                       
      const char* buf="hello write\n";  
      write(1,buf,strlen(buf));        
                                       
      fork();     //创建子进程                                            
             
      return 0;
  }            

        代码解析:开头我写了3个输出函数printf、fprintf、fputs,然后又调用了一个系统调用函数write,它也是一个将内容写入到指定的文件流函数。我将这4个函数全都设为输出到stdout标准输出流(屏幕)中去。然后调用了fork函数,创建了一个子进程后,进程就退出了。

情况1:运行该程序

         结果共输出四句话,代码程序无误。

 

情况2:此时我将该程序运行的结果输出重定向到一个文本文件中:

./test > 123.txt语句的作用:将./test运行的结果打印到123.txt文本文件中,然后我们来看一看该文本文件中的内容,发现一共有7条语句!通过上面运行test可执行文件的结果也不过才输出了4条语句,可为什么这次输出了7条语句?

        通过结果分析:我们发现关于C语言库函数printf、fprintf、fputs的语句各有两条,而系统调用函数write的语句只有一条。根据结果推测出只有采用C语言函数的语句会额外多出来一份!

        而导致这个奇怪问题的“罪魁祸首”就是缓冲区了。我们在学习C语言的过程中,经常用到printf函数,该函数的作用就是将想所写的内容打印到屏幕中去,而在打印的过程中其实还需要经历很重要的一步:会经过一段空间,而这段空间就是缓冲区。 例如,printf("hello world\n"); 我们想要在屏幕中打印字符串"hello world\n",就需要先将该字符串放到缓冲区中,然后在缓冲区中,读取传过来的" hello world\n",在读到\n字符时,缓冲区就立即刷新到屏幕中。遇到\n字符就刷新缓冲区的情况是显示器stdout的特性,它是行缓冲方式。

        而fprintf、fputs函数也是如此,都需要经过缓冲区,而一遇到\n字符都会立即刷新缓冲区到屏幕中对于系统调用函数write,它就不会经过缓冲区,而是直接打印到屏幕中去。原因就是缓冲区是printf、fprintf、fputsC库函数特有的,而系统调用没有。之后便是fork函数了,作用是创建一个子进程,这时该进程中共有父子两个进程,注:创建出的子进程会继承到父进程的大部分属性数据哟!

        因为在情况1中,三个C语言输出函数都是将各自的内容打印到stdout中,且都有\n字符,根据显示器行缓冲的策略可知:这些内容一旦经过缓冲区就立即被刷新到了屏幕中,缓冲区的内容就会被清空了,子进程继承父进程的属性时,没有继承到缓冲区内容。

        而在情况2中,输出重定向从stdout到了文本文件123.txt中即使C库函数有\n字符,缓冲区也不会立即刷新到文件中,因为文件的刷新策略是全缓冲,只有当缓冲区满了或者进程退出了才会刷新缓冲区,而对于系统调用write来说,它由于没有缓冲区,所以直接输出内容到文件中了。

        于是当fork创建出子进程后,子进程会继承父进程的属性数据(包括缓冲区中存留的内容),正好遇到了留在缓冲区的3行内容还没刷新到指定文件的情况,于是根据写实拷贝的原理,子进程也拥有了缓冲区的3行函数输入的内容,之后便是执行return 0;当父子进程中的某一个先退出后(谁先谁后退出没有关系),触发了文件的缓冲刷新策略,该进程的缓冲区立即刷新到文件中,然后另一个进程也退出了,也触发了文件的缓冲刷新策略,于是共有两份的C库函数的内容和一份系统调用函数的内容——7行被输出到了该文件中,那么最终的原因也就理清了。

注:上文提到的缓冲区的刷新策略下面会讲~~


二.为什么要有缓冲区?

同样的,我来举个例子方便大家理解缓冲区的意义:

        小明和小丽都是大学生,他们是男女朋友关系,由于高考分数的差异,他们分别在北京和陕西上大学,将最近是期末考试,他发现过两天是小丽的生日且想要送小丽生日礼物,但时间紧迫,做法1:于是他买好后带着礼物赶忙从北京坐飞机、动车、出租来到了小丽所在的学校将礼物送给她,然后飞快的回到学校应对考试;做法2:他将买好的礼物拿到快递站中让快递员给他邮过去。

 

这个案例说明了什么?

         说明了小明亲自带着礼物去很花时间;而使用快递邮过去的方法节省了大量时间。(注:这里不谈男朋友亲自送过来怎么怎么样.....,望大家不要杠~哈哈哈)。

         所以快递公司就好比是缓冲区,小明是进程,小丽是磁盘文件,这就相当于是进程通过缓冲区对磁盘文件进行数据 I/O的高效传输行为。

        但是邮快递也是需要注意这样一个问题,你去快递公司邮东西时,快递员无法立即给你打包好出发,只有当去往该地的快递货车在出货量达满或者达到一定要求后才会出发。

        假如有一块1024字节大小的数据,是一次性写到磁盘(外设)好呢还是少量多次的写到磁盘好呢?很明显,是一次写入磁盘的效率是最高的,是最省时间的。因为写内容到指定文件中是需要先访问的,先访问就需要调用open、fopen函数,调用的次数多了,效率也就下去了。

       

于是引出了缓冲区的刷新策略:

策略1:立即刷新 —— 无缓冲       ——  传输的内容很少,所以直接刷新缓冲区到指定地方。

策略2:行缓冲     ——显示器(给用户看的)——        行缓冲是几行几行的从缓冲区中刷新内容,人们的习惯是一次几行几行的看,若是一次输出一整篇文章,反而看的有些痛苦。

策略3:全缓冲       ——磁盘文件   —— 效率最高,会将内容全都传输到文件中去。

        我们大部分用的就是行缓冲或者全缓冲策略,在行缓冲区中使用C库输出函数遇到\n字符,或者使用系统调用函数fflush(stdout);强制刷新缓冲区的内容,或者等到进程退出也有可以刷新缓冲区内容。

        这就是缓冲区存在的意义,能够给使用者节约大量的时间。 假如进程对磁盘文件进行数据IO的时间为1秒,那么有990毫秒是在进行数据的拷贝,而10毫秒是在进行数据的传输。


三.缓冲区在哪

        根据上面的案例可知:我们所说的缓冲区是语言级别的缓冲区,它一定不在系统内核中,否则write函数在重定向到文件时它也会出现两次。因为printf、fprintf、fputs是会自带缓冲区的,这是C库函数自带的,而我们又知道在使用fopen函数访问文件时,它的返回值是FILE*的指针,所以缓冲区就和C语言给我们提供的FILE类型有关:

        FILE也是一种数据类型,说明它也是一个结构体,我们可以在Linux的路径:/usr/include/libio.h源代码中查找FILE:

所以缓冲区就在struct _IO_FILE结构体内。

        而write等系统调用函数是没有FILE数据类型的,所以它们也就没有缓冲区。 

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

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

相关文章

【学习笔记】 科目一之计算题篇

【学习笔记】 科目一之计算题篇 三点估算PERT 三点估算期望:(悲观+4*最可能+乐观)/6三点估算标准差:(悲观-乐观)/6正态分布四个数:34.1%,13.65%,2.1%,0.15%决策树 分叉计算注意:成本越小越好投资回报:收益-投入投资回报率(ROI)=收益-投入/投入投资回收期 静态回…

算法--PageRank

概念 PageRank是Google提出的算法&#xff0c;用于衡量特定网页相对于搜索引擎索引中的其他网页而言的重要程度。是Google创始人拉里佩奇和谢尔盖布林于1997年创造的PageRank实现了将链接价值概念作为排名因素。 GOOGLE PageRank并不是唯一的链接相关的排名算法&#xff0c;而…

如何提升 MySQL 的查询速度?

&#x1f482; 个人网站:【海拥】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 目录 前言优化数据库结构1 使…

QT(一) 安装 QT(二)

第一章 &#xff1a; Qt 安装 下载地址安装 打开 cmd 运行镜像 &#xff1a; qt-unified-windows-x64-4.6.0-online.exe --mirror https://mirrors.aliyun.com/qt Hello 因为是qmake 所以是.proCtrl R 直接运行 第二章 GUI程序设计基础 main文件 *.ui : 有UI设计器自动生成…

【工具】Spring 历史官方文档理解(持续更新)

文章目录 [1] Spring Framework 5.2.24CoreAOP 概念AspectJoin pointAdvicePointcutIntroductionTarget objectAOP proxyWeaving Spring AOPAspectJ官方 demo 学习 Pointcut 表达式官方 demo 学习 Advice 声明官方 demo 学习 Introductions &#xff08;接口拓展&#xff09;AO…

0004Java程序设计-SSM+JSP医院挂号系统

摘 要 医院挂号&#xff0c;一直以来就是困扰医院提高服务水平的重要环节&#xff0c;特别是医疗水平高、门诊访问量高的综合型医院&#xff0c;门诊拥挤就成了普遍现象。因此&#xff0c;本文提出了医院挂号系统。预约挂号&#xff0c;是借助信息化的技术&#xff0c;面向全社…

代码随想录二刷 day32 | 贪心之 122.买卖股票的最佳时机II 55. 跳跃游戏 45.跳跃游戏II

这里写目录标题 122.买卖股票的最佳时机II55. 跳跃游戏45.跳跃游戏II 122.买卖股票的最佳时机II 题目链接 解题思路&#xff1a; 首先要清楚两点&#xff1a; 只有一只股票&#xff01;当前只有买股票或者卖股票的操作 想获得利润至少要两天为一个交易单元。 代码如下&#x…

【Unity每日一记】时间Time类-做时间管理大师

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;uni…

独立开发变现周刊(第92期):创建一个年收入350万美元的小工具,1000万至1500万美元出售...

分享独立开发、产品变现相关内容&#xff0c;每周五发布。 目录 1、Vercel AI: 使用React, Svelte和Vue快速构建 AI 驱动的应用2、Novel&#xff1a;AI自动补全功能的Notion风格所见即所得编辑器3、Notionbase: 通过Notion轻松建立你的AI聊天机器人4、Plasmo: 一款功能强大的浏…

Python入门教程+项目实战-14.1节-程序实战-二分查找算法

目录 14.1.1 理解函数类型 14.1.2 函数的定义 14.1.3 函数的形参&#xff0c;实参&#xff0c;以及调用 14.1.4 函数的返回值 14.1.5 函数的命名规范 14.1.6 知识要点 14.1.7 系统学习python 14.1.1 理解函数类型 在Python中&#xff0c;函数也是一种数据类型。在理解函…

C++——详解类模板与友元函数

纵有疾风起&#xff0c;人生不言弃。本文篇幅较长&#xff0c;如有错误请不吝赐教&#xff0c;感谢支持。 &#x1f4ac;文章目录 类模板与友元函数1️⃣非模板友元函数2️⃣约束模板友元函数3️⃣非约束模板友元函数 类模板与友元函数 模板类的友元函数有三类&#xff1a; …

Qt/C++编写手机版本视频播放器和Onvif工具(可云台和录像)

一、前言 用Qtffmpeg写播放器很多人有疑问&#xff0c;为何不用Qt自己的多媒体框架来写&#xff0c;最重要的原因是Qt自带的目前都依赖具体的本地解码器&#xff0c;如果解码器不支持&#xff0c;那就是歇菜的&#xff0c;最多支持个MP4格式&#xff0c;而且在手机上也都是支持…

C国演义 [第七章]

第七章 最长重复子数组题目理解步骤dp含义递推公式初始化为啥dp数组如此奇怪 遍历顺序 代码 最长公共子序列题目理解步骤dp含义递推公式初始化遍历顺序 代码 总结 最长重复子数组 力扣链接 给两个整数数组 nums1 和 nums2 &#xff0c;返回 两个数组中 公共的 、长度最长的子…

设计模式之中介者模式笔记

设计模式之中介者模式笔记 说明Mediator(中介者)目录中介者模式示例类图抽象中介者类抽象同事类租房者类房主类具体的中介者角色类测试类 说明 记录下学习设计模式-中介者模式的写法。JDK使用版本为1.8版本。 Mediator(中介者) 意图:用一个中介对象来封装一系列的对象交互。…

剑指 Offer 68 - II. 二叉树的最近公共祖先 / LeetCode 236. 二叉树的最近公共祖先(搜索与回溯)

题目&#xff1a; 链接&#xff1a;剑指 Offer 68 - II. 二叉树的最近公共祖先&#xff1b;LeetCode 236. 二叉树的最近公共祖先 难度&#xff1a;中等 上一题博客&#xff1a;剑指 Offer 68 - I. 二叉搜索树的最近公共祖先 / LeetCode 235. 二叉搜索树的最近公共祖先&#xf…

python:并发编程(二十四)

前言 本文将和大家一起探讨python并发编程的实际项目&#xff1a;win图形界面应用&#xff08;篇六&#xff0c;共八篇&#xff09;&#xff0c;系列文章将会从零开始构建项目&#xff0c;并逐渐完善项目&#xff0c;最终将项目打造成适用于高并发场景的应用。 本文为python并…

Pandas进阶修炼120题-第二期(Pandas数据处理,21-50题)

文章目录 Pandas进阶修炼120题第二期 Pandas数据处理21.读取本地EXCEL数据22.查看df数据前5行23.将salary列数据转换为最大值与最小值的平均值方法一&#xff1a;正则表达式&#xff08;分别使用apply()&#xff0c;applymap(),map()来实现&#xff09;方法二&#xff1a;apply…

wifi芯片原理

一、在系统中的位置 基带&#xff08;baseband&#xff09; 基带的作用有三个&#xff1a; 1、队列包的管理 2、调试解调 3、CSMA/CA机制 CSMA/CA的全称是Carrier Sense Multiple Access with Collision Avoidance&#xff0c;即载波侦听多路访问&#xff0f;冲突避免。 各…

java——集合框架

文章目录 接口实现&#xff08;类&#xff09;算法1. 排序算法2. 查找算法3. 拷贝算法4. 填充算法5. 比较算法6. 随机算法7. 迭代器算法8. 交集、并集、差集9. 分割集合10. 数组和集合的互转 集合框架是一个用来代表和操纵集合的统一架构。所有的集合框架都包含如下内容&#x…

C++手撕红黑树

目录&#xff1a; 红黑树的概念红黑树的性质红黑树节点的定义 红黑树结构红黑树的插入操作红黑树的验证红黑树的代码实现 红黑树的删除红黑树与AVL树的比较红黑树的应用 总结 红黑树的概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结…