当前位置: 首页 > news >正文

【Linux取经路】文件系统之缓冲区

在这里插入图片描述

文章目录

  • 一、先看现象
  • 二、用户缓冲区的引入
  • 三、用户缓冲区的刷新策略
  • 四、为什么要有用户缓冲区
  • 五、现象解释
  • 六、结语

一、先看现象

#include <stdio.h>
#include <string.h>
#include <unistd.h>int main()
{const char* fstr = "Hello fwrite\n";const char* str = "Hello write\n";printf("Hello printf\n");fprintf(stdout, "Hello fprintf\n");fwrite(fstr, strlen(fstr), 1, stdout); // 返回值是写入成功的快数write(1, str, strlen(str)); // 返回值是写入成功的字节数// fork();return 0;
}

在这里插入图片描述
结构分析:带 fork 的输出重定向最终把有一些内容向 log.txt 文件中写入了多次,并且打印顺序也有所不同。

int main()
{const char* fstr = "Hello fwrite";const char* str = "Hello write";printf("Hello printf");fprintf(stdout, "Hello fprintf");fwrite(fstr, strlen(fstr), 1, stdout); // 返回值是写入成功的快数close(1);// write(1, str, strlen(str)); // 返回值是写入成功的字节数// fork();return 0;
}

在这里插入图片描述
结果分析:代码中只使用了库函数向显示器中进行写入,并且在字符串的结尾没有加 \n,在最后面将标准输出对应的文件描述符进行了关闭,最终显示器上什么也没有。上一段代码在字符串的结尾加上了 \n 最终字符串被成功的打印到了屏幕上。

int main()
{const char* str = "Hello write";write(1, str, strlen(str)); // 返回值是写入成功的字节数close(1);return 0;
}

在这里插入图片描述
结果分析:字符串的结尾依然不加 \n,但是这一次采用系统调用接口,最后仍然将标准输出对应的文件描述符进行关闭,这一次字符串被成功的打印了出来。

二、用户缓冲区的引入

write 为什么能将不带 \n 的字符串写入到显示器文件中。首先我们需要明确一点进程打开的每一个文件都有一个属于自己的操作系统级别的文件缓冲区,该缓冲区的存在,可以减少对外设的读写操作以提高计算机的效率。举个栗子,在一个进程中向磁盘里的同一个文件进多次行写入,文件缓冲区的存在,可以将每次写入的内容先存储在文件缓冲区中,最后在程序退出或者调用 close 的时候,一次性将文件缓冲区中的所有内容刷新到磁盘。如果没有该文件缓冲区,那在进程里对文件进行 n 次写操做,就要对应 n 次向磁盘的写操作,CPU 和外设之间是存在非常大的速度差的,这样效率会非常低。

write 作为系统调用接口,它就是直接向文件缓冲区中写入,最后在调用 close 接口或者程序退出的时候,会将文件缓冲区的内容刷新到对应的外设中。

printffprintffwrite 底层一定是封装了 write 系统调用接口,那为什么使用 write 系统调用接口就可以将字符串写入到显示器,使用 C 库函数没能把字符串写入到显示器文件?原因在进度条的那篇文章中讲过,我们使用的这些 C 库函数,是把字符串写入到了缓冲区中,这个缓冲区和上面的文件缓冲区有所不同,这里说的缓冲区是 C 语言给我们提供的语言层面的缓冲区,也叫做用户级缓冲区\n 具有刷新用户级缓冲区的作用,因此不加 \n 并且在程序结束前将显示器对应的文件描述符进行了关闭,最终就导致字符串在用户级缓冲区中,没有被刷新到文件缓冲区,所以屏幕上就什么也没有。这里我们可以肯定,在这些 C 库函数中,并不是立即调用 write 接口,而是在遇到 \n 后才去调用 write 接口将用户缓冲区的内容刷新到文件缓冲区中。

在这里插入图片描述

总结:使用 C 系统调用接口向文件中写入,写入的内容先被存储在用户缓冲区中,在合适的时候(遇到 \n)才会进行刷新,这里刷新的本质是调用 write 将数据从用户缓冲区写入内核。

之前说的 exit 会刷新缓冲区,其实就是刷新用户缓冲区,因为 exit 作为 C 库函数,可以看见用户缓冲区,而 _exit 作为系统调用接口,无法看到语言层面的用户缓冲区,因此也就无法刷新用户缓冲区。

三、用户缓冲区的刷新策略

  • 无缓冲:直接刷新,数据不在用户缓冲区中停留。

  • 行缓冲:不刷新,直到碰到 \n

  • 全缓冲:缓冲区满了才刷新。

所谓刷新就是调用 write 接口将数据写入操作系统中的文件缓冲区。显示器文件对应采用的就是行缓冲,向磁盘文件中写入采用的是全缓冲。进程在退出的时候也会刷新用户缓冲区,还可以调用 fflush 进行刷新。

四、为什么要有用户缓冲区

  • 解决效率问题,缓冲区就像菜鸟驿站,不需要我们自己坐火车坐飞机去送东西,而是直接交给菜鸟驿站,然后就可以干自己的事情了,菜鸟驿站可以选择攒上一大批快递然后统一寄送出去。用户缓冲区的存在本质上提高了 C 语言的效率,也就是提高了用户的效率,因为 C 语言是程序员在使用,在使用 C 库函数进行文件写入时,大部分情况只需要把数据交给缓冲区,然后就可以快速的返回,不需要每一次都亲力亲为的去和操作系统打交道。

  • 配合格式化,有些和文件写入相关的 C 库函数是格式化输出函数,在我们看来,它可以写入整形、符点型,但是最终都是以字符串的形式进行写入。格式化就是将类型全都转化成字符串,先写入到用户缓冲区,用户缓冲区中存的一定都是字符串。

用户缓冲区,有进也有出,将数据写入到用户缓冲区中就就叫做进,将用户缓冲区中的数据刷新到内核中的文件缓冲区中,被刷新的数据就可以从用户缓冲区中删掉,这就叫做出。用户缓冲就像就像水流一样源源不断,流的概念就是因此而来。

小TipsFILE 里面就有对应打开文件的缓冲区字段和维护信息。每个被进程打开文件都有自己对应的文件缓冲区。FILE 对象属于用户,用户缓冲区可以看作是在堆上申请的一块空间。

五、现象解释

这下再来解释上面代码中有 fork 然后重定向,写入了多次的原因。首先重定向后,将本来向显示器文件写入的内容,写到了磁盘文件,显示器文件的缓冲区采用行缓冲,即遇到 \n 就会刷新,而磁盘文件采用的是全缓冲,当缓冲区满了才刷新。因此在重定向后,会把三条 C 库函数写入的内容全部保存到缓冲区中,然后调用 fork 创建子进程,此时父子进程代码共享,数据写时拷贝,在程序退出的时候回去刷新用户缓冲区,上面说过,刷新就是将用户缓冲区中的数据写入到内核,然后将用户缓冲区中的内容清空,上面还说过,缓冲区就是在堆上申请的一段空间,可以看作数据部分,因为要删除数据,所以就会进行写时拷贝,此时之前父进程用户缓冲区中的内容就会给子进程拷贝一份,然后父子进程都执行刷新动作,各自刷新自己的缓冲区数据,这就是为什么最终出现多份的原因。没有重定向,只向显示器打印四条消息,是因为显示器采用的是行刷新策略,在调用 fork 前,对应的字符串就已经被刷新出去了。在 fork 的时候,父进程的用户缓冲区中是空的,什么也没有。

磁盘文件全缓冲验证

int main()
{const char* fstr = "Hello fwrite\n";const char* str = "Hello write\n";printf("Hello printf\n");sleep(2);fprintf(stdout, "Hello fprintf\n");sleep(2);fwrite(fstr, strlen(fstr), 1, stdout); // 返回值是写入成功的快数sleep(2);write(1, str, strlen(str)); // 返回值是写入成功的字节数sleep(5);fork();return 0;
}

在这里插入图片描述
分析:最先将 write 内容写入到文件中,因为它是直接写入到文件缓冲区,而剩下的 C 库函数对应的内容是统一一次全部刷新到内核,即使每个字符串后面都有 \n,但最后还是统一全部刷新,这就证明了磁盘文件采用的是全刷新策略。

六、结语

今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下,春人的主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是春人前进的动力!

在这里插入图片描述

相关文章:

【Linux取经路】文件系统之缓冲区

文章目录 一、先看现象二、用户缓冲区的引入三、用户缓冲区的刷新策略四、为什么要有用户缓冲区五、现象解释六、结语 一、先看现象 #include <stdio.h> #include <string.h> #include <unistd.h>int main() {const char* fstr "Hello fwrite\n"…...

华为OD机试真题-查找接口成功率最优时间段-2023年OD统一考试(C卷)--Python3--开源

题目&#xff1a; 考察内容&#xff1a; for 时间窗口list(append, sum, sort) join 代码&#xff1a; """ 题目分析&#xff1a;最长时间段 且平均值小于等于minLost同时存在多个时间段&#xff0c;则输出多个&#xff0c;从大到小排序未找到返回 NULL 输入…...

缓存篇—缓存雪崩、缓存击穿、缓存穿透

缓存异常会面临的三个问题&#xff1a;缓存雪崩、击穿和穿透。 其中&#xff0c;缓存雪崩和缓存击穿主要原因是数据不在缓存中&#xff0c;而导致大量请求访问了数据库&#xff0c;数据库压力骤增&#xff0c;容易引发一系列连锁反应&#xff0c;导致系统奔溃。不过&#xff0…...

Python实现视频转音频、音频转文本的最佳方法

文章目录 Python实现视频转音频和音频转文字视频转音频步骤 1&#xff1a;导入moviepy库步骤 2&#xff1a;选择视频文件步骤 3&#xff1a;创建VideoFileClip对象步骤 4&#xff1a;提取音频步骤 5&#xff1a;保存音频文件 音频转文字步骤 1&#xff1a;导入SpeechRecognitio…...

阿里云SSL免费证书到期自动申请部署程序

阿里云的免费证书只有3个月的有效期&#xff0c;不注意就过期了&#xff0c;还要手动申请然后部署&#xff0c;很是麻烦&#xff0c;于是写了这个小工具。上班期间抽空写的&#xff0c;没有仔细测试&#xff0c;可能存在一些问题&#xff0c;大家可以自己clone代码改改&#xf…...

Vue全局事件防止重复点击(等待请求)【进阶版】

继《Vue全局指令防止重复点击&#xff08;等待请求&#xff09;》之后&#xff0c;感觉指令方式还是不太友好&#xff0c;而且嵌套闭包比较麻烦&#xff0c;于是想到了Vue的全局混入&#xff0c;利用混入&#xff0c;给组件绑定click事件。 一、实现原理 与指令方式大致一样&…...

C#程序反编译经验总结

1. 反编译出的代码有问题时&#xff0c;可以用多个反编译工具之间的代码相互印证。&#xff08;比如.net reflector 与ILSpy&#xff09; 2. 有时Visual Studio编译的错误信息不明确时, 可以msbuild编译程序&#xff0c;msbuild的错误信息相对完整一些。 2.1 编译错误&#xf…...

Android系统启动流程

android的启动流程是从底层开始进行的&#xff0c;具体如下所示&#xff1a; Android是基于Linux内核的系统&#xff0c;Android的启动过程主要分为两个阶段&#xff0c;首先是Linux内核的启动&#xff0c;然后是Android框架的启动。 可以将Andorid系统的启动流程分为以下五个…...

Flask——基于python完整实现客户端和服务器后端流式请求及响应

文章目录 本地客户端Flask服务器后端客户端/服务器端流式接收[打字机]效果 看了很多相关博客&#xff0c;但是都没有本地客户端和服务器后端的完整代码示例&#xff0c;有的也只说了如何流式获取后端结果&#xff0c;基本没有讲两端如何同时实现流式输入输出&#xff0c;特此整…...

crmeb多门店商城系统二次开发 增加车辆车牌搜索功能、车辆公里数

1、增加的数据库 ALTER TABLE eb_store_order ADD cart_number VARCHAR(255) NOT NULL DEFAULT COMMENT 车牌 AFTER erp_order_id, ADD curmileage VARCHAR(255) NOT NULL DEFAULT COMMENT 当前里程 AFTER cart_number; ALTER TABLE eb_store_cart ADD cart_number VARCHAR(…...

深度好文|关于人类智能与自主系统

上个世纪 50 年代&#xff0c;在二战结束没多久&#xff0c;人们开始研究和设计智能系统。作为信息学的分支&#xff0c;人类开始了最早对于人工智能的研究。时间来到 60 年代&#xff0c;人们对于计算机的发展充满了信心&#xff0c;人们断言“20年内机器能够做任何人所能做的…...

防火墙内容安全笔记

目录 DFI和DPI IDS和IPS 签名 AV URL过滤 HTTPS过滤 内容过滤 文件类型过滤 文件内容过滤 邮件过滤 VPN概述 DFI和DPI DFI和DPI技术 --- 深度检测技术 DPI DPI --- 深度包检测技术 --- 主要针对完整的数据包&#xff08;数据包分片&#xff0c;分段需要重组&#…...

应用于温度报警器中的高精度温度传感芯片

温度报警器通常由温度传感器、控制电路和报警装置组成。温度传感器能够将温度变化转换为电信号&#xff0c;控制电路则对这些信号进行处理&#xff0c;当检测到的温度达到或超过预设的报警阈值时&#xff0c;报警装置会通过声音、灯光或其他方式发出警报&#xff0c;以提醒用户…...

微信小程序swiper 视频中间大,两边小,轮播滑到中间视频自动播放组件教程

静态效果&#xff1a; 进入下面小程序可以体验效果&#xff0c;点击底部 看剧 栏目 一、创建小程序组件 二、代码 1、WXML <view class"swiper-wrapper"><swiperclass"main-sw"autoplay"{{false}}"circular"{{true}}"inte…...

ARM服务器上部署zookeeper集群

由于ARM服务器上部署zookeeper集群,会存在加载不到主类问题,现在把遇到的问题进行总结下,问题如下: [rootnode206 apache-zookeeper-3.5.10]# bin/zkServer.sh start ZooKeeper JMX enabled by default Using config: /data1/software/apache-zookeeper-3.5.10/bin/../conf/…...

利用Ubuntu22.04启动U盘对电脑磁盘进行格式化

概要&#xff1a; 本篇演示利用Ubuntu22.04启动U盘的Try Ubuntu模式对电脑磁盘进行格式化 一、说明 1、电脑 笔者的电脑品牌是acer(宏碁/宏基) 开机按F2进入BIOS 开机按F12进入Boot Manager 2、Ubuntu22.04启动U盘 制作方法参考笔者的文章&#xff1a; Ubuntu制作Ubun…...

Nginx基础入门

一、Nginx的优势 nginx是一个高性能的HTTP和反向代理服务器&#xff0c;也是一个SMTP&#xff08;邮局&#xff09;服务器。 Nginx的web优势&#xff1a;IO多路复用&#xff0c;时分多路复用&#xff0c;频分多路复用 高并发&#xff0c;IO多路复用&#xff0c;epoll&#xf…...

分布式和微服务

分布式和微服务是两个不同的概念。 分布式系统是说多个独立的计算机或服务器组成的系统&#xff0c;这些计算机通过网络进行通信和协作&#xff0c;共同完成一个任务或提供一个服务。 分布式系统的目标是通过协作实现高性能、高可用性和高扩展性。 微服务是一种架构风格&…...

【无标题】学习Markdown

https://shadows.brumm.af 欢迎使用Markdown编辑器 你好&#xff01; 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章&#xff0c;了解一下Markdown的基本语法知识。 新的改变 我们对Markdown编辑器进行了一些…...

由于 vscode 版本更新为 1.86.1引起的相关问题。

通过vscode ssh来远程连接linux服务器的代码&#xff0c;由于vscode 1.86.1的更新&#xff0c;在连接服务器时就开始报 两个错误了&#xff1a; Missing GLIBCXX > 3.4.25! Missing GLIBC > 2.28! lwd192.168.66.148s password: 075b6e8e3a87: runningMissing GLIBCXX &g…...

【网络】每天掌握一个Linux命令 - iftop

在Linux系统中&#xff0c;iftop是网络管理的得力助手&#xff0c;能实时监控网络流量、连接情况等&#xff0c;帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

FFmpeg 低延迟同屏方案

引言 在实时互动需求激增的当下&#xff0c;无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作&#xff0c;还是游戏直播的画面实时传输&#xff0c;低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架&#xff0c;凭借其灵活的编解码、数据…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面

代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http…...

Python爬虫(一):爬虫伪装

一、网站防爬机制概述 在当今互联网环境中&#xff0c;具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类&#xff1a; 身份验证机制&#xff1a;直接将未经授权的爬虫阻挡在外反爬技术体系&#xff1a;通过各种技术手段增加爬虫获取数据的难度…...

【Java学习笔记】BigInteger 和 BigDecimal 类

BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点&#xff1a;传参类型必须是类对象 一、BigInteger 1. 作用&#xff1a;适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...

Netty从入门到进阶(二)

二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架&#xff0c;用于…...

【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)

LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 题目描述解题思路Java代码 题目描述 题目链接&#xff1a;LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...