从内核到应用层:Linux缓冲机制与语言缓冲区的协同解析

系列文章目录


文章目录

  • 系列文章目录
  • 前言
  • 一、缓冲区
    • 1.1 示例1
    • 1.2 缓冲区的概念
  • 二、缓冲区刷新方案
  • 三、缓冲区的作用及存储


前言

上篇我们介绍了,文件的重定向操作以及文件描述符的概念,今天我们再来学习一个和文件相关的知识-----------用户缓冲区。
在操作系统中,缓冲区是实现高效资源管理的关键进制,缓冲区可以帮助用户、系统暂存读取及写入数据,规避了用户频繁的I/O操作,可以很好的提高系统的性能和用户的体验。


一、缓冲区

由于我们对缓冲区接触的比较少,所以在讲解这部分知识时,我们会引入大量的代码示例,后面我们会对这些示例及结果逐一分析。

1.1 示例1

下列函数均向标准输出打印

    1 #include<stdio.h>2 #include<string.h>3 #include<unistd.h>4 int main()5 {6   char *fstr="hello fwrite\n";7   char *wstr="hello witer\n";8   //c函数9   printf("hello printf\n");10   fprintf(stdout,"hello fprintf\n");11   fwrite(fstr,1,strlen(fstr),stdout);                                                                                           12   //系统调用接口                                                                                                       13   write(1,wstr,strlen(wstr));                                                                                          14   return 0;                                                                                                            15 }   

执行结果1:
在这里插入图片描述
将输出重定向至log1.txt

./myfile >log1.txt

执行结果2:
在这里插入图片描述
到现在执行结果都是我们可以接受的,不要着急继续向下看。

    1 #include<stdio.h>2 #include<string.h>3 #include<unistd.h>4 int main()5 {6   char *fstr="hello fwrite\n";7   char *wstr="hello witer\n";8   //c函数9   printf("hello printf\n");10   fprintf(stdout,"hello fprintf\n");11   fwrite(fstr,1,strlen(fstr),stdout);12   //系统调用接口13   write(1,wstr,strlen(wstr));14   fork();                                                                                                                       15   return 0;                                                 16 }       

我们在文件末尾处创建了一个子进程,重复上面实验:
执行结果3:
在这里插入图片描述
将文件重定向输入到log2.txt

./myfile >log2.txt

执行结果4:
在这里插入图片描述
通过和前三次执行结果对比,我们可以看到向文件log2.txt打印的结果,库函数打印了两次,系统调用接口只打印了一次,通过对比结果2和结果4,我们可以知道一定是fork()函数产生的影响,那么为什么fork()没有对结果3产影响呢?带着这两问题我们继续往下看。

1.2 缓冲区的概念

我们接着来看下面这两个示例:

    1 #include<stdio.h>                                            2 #include<string.h>                                           3 #include<unistd.h>                                           4 int main()                                                   5 {                                                            6   char *fstr="hello fwrite\n";                               7   char *wstr="hello witer\n";                                8   //c函数                                                    9   printf("hello printf\n");                                  10   fprintf(stdout,"hello fprintf\n");                         11   fwrite(fstr,1,strlen(fstr),stdout);                        12   //系统调用接口13   write(1,wstr,strlen(wstr));                                                                                                   14   //fork();15   close(1);16   return 0;                      17 }

当执行完上面四个调用后我们使用close()函数关闭文件描述符1的文件。
在这里插入图片描述
此时并没有对程序执行结果造成影响,下面我们将\n全部去掉,继续执行程序。

    1 #include<stdio.h>2 #include<string.h>3 #include<unistd.h>4 int main()5 {6   char *fstr="hello fwrite";7   char *wstr="hello witer";8   //c函数9   printf("hello printf");10   fprintf(stdout,"hello fprintf");                                                                                              11   fwrite(fstr,1,strlen(fstr),stdout);12   //系统调用接口13   write(1,wstr,strlen(wstr));14   //fork();15   close(1);16   return 0;17 }

在这里插入图片描述
可以看到此时只有系统调用接口成功将内容打印了出来,这又是怎么回事呢,相信大家早在学习C语言时,就听说过缓冲区,下面我们就来慢慢的回答上面一系列问题。

在这里插入图片描述

结合下面的解释看这个图,一定要仔细看,精华全在图上!!!!!
首先我们要清楚的一点是,printf/fprintf/fwrite全部是封装的系统调用write。在我们的内核中,进程会拥有对于的task_struct结构体,这个结构体对象包含一个文件结构体指针(上篇我们讲了,这里我们认为此时它指向显示器文件的file对象),通过这个文件对象可以找到内核缓冲区,所以的输入、输出,都需要经过这个缓冲区才能到达对应的磁盘中(包含硬件设备),当我们在执行C语言函数时,结果并不会直接打印在屏幕上,而是先存入C语言的缓存区,这一点通过上面的示例也能感受到,当程序执行到合适的时间,就会调用系统接口writewrite通过文件描述符找到对应的文件对象,然后才能将c语言缓冲区的内容输出到内核缓冲区,当数据到达内核缓冲区,符合条件,就会被刷新到显示器上(磁盘),这个条件我们后面会介绍。

当程序执行系统调用write时,它会根据我们给他提供的文件描述符,找到对应的文件对象,直接将内容输出到内核缓冲区。有了这些概念,我们来分析上面代码。
在这里插入图片描述
当我们执行的程序执行c函数时,它会先将内容存入C语言的缓冲区,但程序执行系统调用时,他是将内容直接刷新到了系统缓冲区,程序继续向下执行close(1)显示器文件被关闭,此时c函数调用系统调用write想要将处在C语言缓冲区的数据输出道系统缓冲区,但是此时write已经无法找到显示器结构体对象了,素以无法实现,最后程序结束,系统缓冲区被刷新到显示器,结果表现为只有系统调用打印成功。到了这里我们算是回答了一个问题,那么为什么打印数据后加\n,就可以输出成功呢?要回答这个问题我们就要来谈一谈缓冲区刷新方案了。

二、缓冲区刷新方案

在这里我们只谈C语言的缓冲区刷新方案,我们将这种语言及的缓冲区称为用户及缓冲区(每个语言都会提供)。
缓冲区刷新方案主要有三种:

  • 无缓冲-------直接刷新
  • 行缓冲--------不刷新,直到碰见\n(一般为向显示器打印时采用)
  • 全缓冲----------缓冲区满了,才刷新(一般为向文件打印时采用)

此外当程序执行结束后也会进行刷新。

现在我们可以来回答为什么,这里不受文件关闭的影响了。
在这里插入图片描述

当程序执行C函数时,会先将数据存入用户及缓冲区,但用户及缓冲区判断数据存在\n就会立即调用write将数据刷新到系统的缓冲区(此时文件还没有关闭)。

下面我们来回答这个问题

在这里插入图片描述
为什么我们对程序进行重载后,C函数的结果打印了两次。
在这里插入图片描述
当执行这个程序时,我们对他它重定向到文件,此时缓冲区刷新方案由之前的行缓冲变为全缓冲,所以c函数的执行结果会被存储在用户级缓冲区,而write的执行结果则会直接存入系统缓冲区,此时创建子进程,程序结束时,父进程要调用write将用户级缓冲区数据刷新到系统缓冲区上(这个行为会将用户及缓冲区数据清空),触发写时拷贝,子进程结束后也会将数据刷新,这时就有两份数据打印到了文件中。

不知道大家有没有注意到,我们上面例子的结果打印顺序,也出现了变化,刚刚的这个逻辑同样能解释这个问题,到了这里我们算是将问题都解决了

三、缓冲区的作用及存储

提高用户效率

在我们调用C函数向显示器或文件写入数据时,若没有缓冲区存在,其底层就会不断的去调用write函数,执行效率较低。

配合格式化

我们学习的printf/fprintf等函数,都是格式化输出函数(如使用%d格式化数据)但是在操作系统中并没有整形、浮点型等概念,在向显示器或文件打印时统一被作为字符输出,所以用户级缓冲区的作用就是将数据格式化处理后,再交有系统接口。

在这里插入图片描述
用户级缓冲区存储位置

用户缓冲区实际是被定义再FILE结构体中的。

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

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

相关文章

高通camx IOVA内存不足,导致10-15x持续拍照后,点击拍照键定屏无反应,过一会相机闪退

定屏闪退问题分析思路&#xff1a; 定屏问题如果是相机问题&#xff0c;一般会出现返帧&#xff0c;导致预览卡死。当然还有其他情况&#xff0c;我们先看返帧情况&#xff0c;发现request和result开始都正常&#xff0c;到12:53:05.443038就没有返帧了&#xff0c;定屏了。往…

AI知识补全(十五):AI可解释性与透明度是什么?

名人说&#xff1a;一笑出门去&#xff0c;千里落花风。——辛弃疾《水调歌头我饮不须劝》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 上一篇&#xff1a;AI知识补全&#xff08;十四&#xff09;&#xff1a;零样本…

CentOS 7安装hyperscan

0x00 前言 HyperScan是一款由Intel开发的高性能正则表达式匹配库&#xff0c;专为需要快速处理大量数据流的应用场景而设计。它支持多平台运行&#xff0c;包括Linux、Windows和macOS等操作系统&#xff0c;并针对x86架构进行了优化&#xff0c;以提供卓越的性能表现。HyperSc…

机器学习的一百个概念(9)学习曲线

前言 本文隶属于专栏《机器学习的一百个概念》&#xff0c;该专栏为笔者原创&#xff0c;引用请注明来源&#xff0c;不足和错误之处请在评论区帮忙指出&#xff0c;谢谢&#xff01; 本专栏目录结构和参考文献请见[《机器学习的一百个概念》 ima 知识库 知识库广场搜索&…

macvlan 和 ipvlan 实现原理及设计案例详解

一、macvlan 实现原理 1. 核心概念 macvlan 允许在单个物理网络接口上创建多个虚拟网络接口&#xff0c;每个虚拟接口拥有 独立的 MAC 地址 和 IP 地址。工作模式&#xff1a; bridge 模式&#xff08;默认&#xff09;&#xff1a;虚拟接口之间可直接通信&#xff0c;类似交…

linux文件上传下载lrzsz

lrzsz 是一个在 Linux 系统中用于通过串行端口(如 ZMODEM、XMODEM、YMODEM 等协议)进行文件上传和下载的工具集。它通常用于在终端环境中通过串口或 SSH 连接传输文件。 安装 lrzsz 在大多数 Linux 发行版中,你可以使用包管理器来安装 lrzsz。 Debian/Ubuntu: sudo apt-ge…

单片机学习之SPI

物理层 串行全双工总线 需要四根线&#xff1a;SCLK&#xff08;时钟线&#xff09;&#xff0c;CS&#xff08;片选线&#xff09;、MOSI(主设备输出、从设备输入)&#xff0c;MISO&#xff08;主设备输入&#xff0c;从设备输出&#xff09;。 片选信号 片选信号CS是用来…

大模型应用初学指南

随着人工智能技术的快速发展&#xff0c;检索增强生成&#xff08;RAG&#xff09;作为一种结合检索与生成的创新技术&#xff0c;正在重新定义信息检索的方式&#xff0c;RAG 的核心原理及其在实际应用中的挑战与解决方案&#xff0c;通用大模型在知识局限性、幻觉问题和数据安…

docker-compose部署prometheus+grafana+node_exporter+alertmanager规则+邮件告警

目录 一.docker-compose文件 二.配置文件 三.文件层级关系&#xff0c;docker-compose和配置文件位于同级目录 四.node_exporter页面json文件 五.效果展示 prometheusalertmanager邮件告警 grafana面板效果 六.涉及离线包 一.docker-compose文件 [rootsulibao prometh…

AI设计再现新引擎,科技创新又添新动能——广东省首家行业AI设计工程中心获批成立

近期&#xff0c;大捷智能科技&#xff08;广东&#xff09;有限公司&#xff08;以下简称“大捷智能”&#xff09;凭借其在人工智能与智能制造领域的突出研发实力与创新科技成果&#xff0c;由广东省科技厅批准设立“广东省模具智能设计与智能制造工程技术研究中心”。 广东省…

【MongoDB + 向量搜索引擎】MongoDB Atlas 向量搜索 提供全托管解决方案

在代码审计项目中&#xff0c;MongoDB可以用于存储元数据和部分结构化信息&#xff0c;但要高效处理向量相似性搜索&#xff0c;需结合其他工具。以下是具体分析&#xff1a; 1. MongoDB 的适用场景 元数据存储&#xff1a; 存储代码片段的文件路径、行号、语言类型等结构化信…

基于ANSYS 概率设计和APDL编程的结构可靠性设计分析

01 可靠度基本理论 结构的极限状态&#xff1a;整个结构的一部分超过某一特定状态就不能满足设计规定的某一功能要求。结构的极限状态实质上是结构工作状态的一个阀值&#xff0c;如果工作状态超过这一阀值&#xff0c;则结构处于不安全、不耐久或不适用的状态&#xff1b;若工…

CyclicBarrier、Semaphore、CountDownLatch的区别,适用场景

CyclicBarrier、Semaphore 和 CountDownLatch 是 Java 并发包中用于线程协作的工具类&#xff0c;它们虽然都与线程同步相关&#xff0c;但设计目的和使用场景有显著差异。以下是它们的核心区别和典型应用场景&#xff1a; 1. CountDownLatch 核心机制 一次性计数器&#xf…

新能源汽车测试中的信号调理模块:从原理到实战应用

摘要 信号调理模块&#xff08;Signal Conditioning Module, SCM&#xff09;是新能源汽车&#xff08;NEV&#xff09;测试系统中的关键环节&#xff0c;直接影响数据采集的精度与可靠性。本文面向HIL测试工程师、电机测试工程师及整车动力经济性测试工程师&#xff0c;系统性…

Qt5 Mac系统检查休眠

在开发跨平台应用程序时,有时候我们需要检测系统的状态,比如是否处于休眠或唤醒状态。Qt是一个强大的跨平台应用开发框架,支持多种操作系统,包括Windows、Linux、macOS等。在这个场景下,我们关注的是如何在Qt5.10中检测到系统是否休眠以及在Mac上实现这一功能。本文将深入…

RabbitMQ简单介绍和安装

RabbitMQ简单介绍 一.RabbitMQ介绍二.RabbitMQ的作用1.异步解耦2.流量削峰3.消息分发4.延迟通知 三.RabbitMQ安装&#xff08;Ubuntu&#xff09;1.先安装Erlang2.安装RabbitMQ3.安装RabbitMQ的管理界面4.创建虚拟机5.端口号信息 四.工作原理图 一.RabbitMQ介绍 RabbitMQ 是一款…

汇编学习之《call, return指令》

call 指令 call 指令就是调用函数的执行&#xff0c;不过它也是几个指令的组合 第一步通过jmp 函数地址的方式先跳转到函数 第二步通过push 指令将函数地址指令额下一行的指令的地址压入栈中。 我们来验证下 首先打开OllyDbg,导入你的程序&#xff0c;找到一个call 函数&…

接口自动化学习三:参数化parameterize

使用parametrize之前&#xff1a; def add(x,y):return xy class TestAddFunction(object):def test01(self):resadd(2,4)assert 6resdef test02(self):resadd(4,6)assert 10resparametrize参数化之后&#xff1a; import pytest def add(x,y):return xydata[(10,20,30),(200…

全面解析 Mybatis 与 Mybatis-Plus:深入原理、实践案例与高级特性对比

全面解析 Mybatis 与 Mybatis-Plus&#xff1a;深入原理、实践案例与高级特性对比 &#x1f680; 前言一、基础介绍 ✨1. Mybatis 简介 &#x1f50d;2. Mybatis-Plus 简介 ⚡ 二、核心区别与高级特性对比 &#x1f50e;1. 开发模式与配置管理2. 功能丰富度与扩展性3. 自动填充…

【区块链安全 | 第十九篇】类型之映射类型

文章目录 映射类型可迭代映射 映射类型 映射类型使用语法 mapping(KeyType KeyName? > ValueType ValueName?)&#xff0c;映射类型的变量声明使用语法 mapping(KeyType KeyName? > ValueType ValueName?) VariableName。 KeyType 可以是任何内置值类型、bytes、st…