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

news2025/5/10 23:54:29

系列文章目录


文章目录

  • 系列文章目录
  • 前言
  • 一、缓冲区
    • 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.coloradmin.cn/o/2327681.html

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

相关文章

【AI News | 20250403】每日AI进展

AI Repos 1、llm-server-docs 项目提供了一份基于Debian系统的本地语言模型服务器搭建指南&#xff0c;适用于Linux初学者。教程涵盖驱动安装、GPU功耗设置、自动登录配置及开机自启脚本部署等关键步骤&#xff0c;支持Ollama/vLLM等多种OpenAI兼容方案。方案设计强调四大原则…

深入理解SQL中的<>运算符:不等于的灵活运用

在SQL的世界里&#xff0c;数据的筛选与查询是最常见的操作之一。在编写查询语句时&#xff0c;比较运算符是我们不可忽视的工具&#xff0c;其中&#xff0c;<> 运算符作为 不等于 的代表&#xff0c;起着至关重要的作用。它不仅能够帮助我们筛选出符合特定条件的数据&a…

数据清洗的具体内容

&#xff08;一&#xff09;ETL介绍 “ETL&#xff0c;是英文Extract-Transform-Load的缩写&#xff0c;用来描述将数据从来源端经过抽取&#xff08;Extract&#xff09;、转换&#xff08;Transform&#xff09;、加载&#xff08;Load&#xff09;至目的端的过程。ETL一词较…

小家电等电子设备快充方案,XSP15支持全协议和支持MCU与电脑传输数据

随着USB-C的普及&#xff0c;市面上消费者PD充电器越来越多&#xff0c;如何让小家电等电子产品也能够支持PD协议快充呢&#xff1f;就需要加入一颗汇铭达XSP15取电协议芯片&#xff0c;这颗芯片不仅能支持取电&#xff0c;还能通过串口读取充电器支持的最大输出功率和支持外部…

缺页异常导致的iowait打印出相关文件的绝对路径

一、背景 在之前的博客 增加等IO状态的唤醒堆栈打印及缺页异常导致iowait分析-CSDN博客 里&#xff0c;我们进一步优化了D状态和等IO状态的事件的堆栈打印&#xff0c;补充了唤醒堆栈打印&#xff0c;也分析了一种比较典型的缺页异常filemap_fault导致的iowait的情况。 在这篇…

记录学习的第十七天

今天对昨天下午的洛谷蓝桥杯模拟赛和今天早上的力扣周赛进行复盘。 昨天的蓝桥杯模拟赛&#xff0c;硬坐了4个小时&#xff0c;只会做前面的三道入门题。&#x1f625;而且第一道填空题竟然还算错了。其他的五道题我都没啥思路了&#xff0c;实在难受啊&#xff01; Q1:这道题硬…

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

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

Ubuntu 22.04 一键部署openManus

openManus 前言 OpenManus-RL,这是一个专注于基于强化学习(RL,例如 GRPO)的方法来优化大语言模型(LLM)智能体的开源项目,由来自UIUC 和 OpenManus 的研究人员合作开发。 前提要求 安装deepseek docker方式安装 ,windows 方式安装,Linux安装方式

强化学习_Paper_1988_Learning to predict by the methods of temporal differences

paper Link: sci-hub: Learning to predict by the methods of temporal differences 1. 摘要 论文介绍了时间差分方法&#xff08;TD 方法&#xff09;&#xff0c;这是一种用于预测问题的增量学习方法。TD 方法通过比较连续时间步的预测值之间的差异来调整模型&#xff0c;…

Apache httpclient okhttp

学习链接 okhttp github okhttp官方使用文档 SpringBoot 整合okHttp okhttp3用法 Java中常用的HTTP客户端库&#xff1a;OkHttp和HttpClient&#xff08;包含请求示例代码&#xff09; 深入浅出 OkHttp 源码解析及应用实践 httpcomponents-client github apache httpclie…

Docker部署Blinko:打造你的个性化AI笔记助手与随时随地访问

文章目录 前言1. Docker Compose一键安装2. 简单使用演示3. 安装cpolar内网穿透4. 配置公网地址5. 配置固定公网地址 前言 嘿&#xff0c;小伙伴们&#xff0c;是不是觉得市面上那些单调乏味的笔记应用让人提不起劲&#xff1f;今天&#xff0c;我要给大家安利一个超炫酷的开源…

【文献研究】铝对热冲压加热过程中锌氧化的影响

在热冲压过程中&#xff0c;镀锌铁板和镀锌板等镀锌钢板表面发生Zn氧化。为了阐明镀锌层中的Al对Zn氧化的影响&#xff0c;本研究研究了镀锌钢板上添加和不添加Al时形成的ZnO量。发现添加铝后ZnO量减少。对添加铝的镀锌钢板的显微组织分析表明&#xff0c;添加的Al在热冲压后Zn…

Win11本地从零开始部署dify全流程

1.安装wsl和打开Hyper-V功能&#xff08;前置准备&#xff09; 这个是为了支持我们的Docker Desktop运行。 1.1.安装wsl 使用管理员身份运行命令行。 如果显示 “无法与服务器建立连接就执行“&#xff0c;表示没有安装wsl&#xff0c;如果更新成功&#xff0c;那就不用执行…

【HTB】Windwos-easy-Legacy靶机渗透

靶机介绍&#xff0c;一台很简单的WIndows靶机入门 知识点 msfconsole利用 SMB历史漏洞利用 WIndows命令使用&#xff0c;type查看命令 目录标题 一、信息收集二、边界突破三、权限提升 一、信息收集 靶机ip&#xff1a;10.10.10.4攻击机ip&#xff1a;10.10.16.26 扫描TC…

蓝桥杯真题———k倍区间

题目如下 代码如下 记录余数 cnt[0] 1 的初始化是为了处理 空前缀和 说明

无人机等非合作目标公开数据集2025.4.3

一.无人机遥感数据概述 1.1 定义与特点 在遥感技术的不断发展中&#xff0c;无人机遥感数据作为一种新兴的数据源&#xff0c;正逐渐崭露头角。它是通过无人驾驶飞行器&#xff08;UAV&#xff09;搭载各种传感器获取的地理空间信息&#xff0c;具有 覆盖范围大、综合精度高、…

机器视觉--python基础语法

Python基础语法 1. Python标识符 在 Python 里&#xff0c;标识符由字母、数字、下划线组成。 在 Python 中&#xff0c;所有标识符可以包括英文、数字以及下划线(_)&#xff0c;但不能以数字开头。 Python 中的标识符是区分大小写的。 以下划线开头的标识符是有特殊意义的…

司南评测集社区 3 月上新一览!

司南评测集社区 CompassHub 作为司南评测体系的重要组成部分&#xff0c;旨在打创新性的基准测试资源导航社区&#xff0c;提供丰富、及时、专业的评测集信息&#xff0c;帮助研究人员和行业人士快速搜索和使用评测集。 2025 年 3 月&#xff0c;司南评测集社区新收录了一批评…

mac环境中Nginx安装使用 反向代理

安装 如没有Homebrew 先安装Homebrew 国内镜像&#xff1a; /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)" 安装成功后安装nginx&#xff1a; brew install nginx 启动nginx&#xff1a; nginx 或者 brew services st…

实战打靶集锦-36-Deception

文章目录 1. 主机发现2. 端口扫描3. 服务枚举4. 服务探查5. 系统提权6. 写在最后 靶机地址&#xff1a;https://download.vulnhub.com/haclabs/Deception.ova 1. 主机发现 目前只知道目标靶机在192.168.56.xx网段&#xff0c;通过如下的命令&#xff0c;看看这个网段上在线的主…