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

【Linux探索学习】第二十二弹——用户缓冲区:深入解析操作系统中数据交互时的缓冲区机制

 Linux学习笔记:

https://blog.csdn.net/2301_80220607/category_12805278.html?spm=1001.2014.3001.5482

前言:

前面两章我们已经讲了一些文件操作和文件重定向问题,以及一些相关的知识点,比如文件在内存中的存储位置,物理内存和虚拟内存的概念,文件描述符的问题等,今天我们要再学一个与内存有关的概念——用户缓冲区。

在操作系统中,缓冲区是用于存储数据的内存区域。在 Linux 中,用户缓冲区通常指的是由用户空间应用程序分配和管理的内存区域,用来存储从外部设备读取或写入的数据。操作系统通过缓冲区来避免频繁地进行 I/O 操作,提高效率,同时保证数据的完整性和一致性。

本篇主要通过一些代码示例来帮助大家理解缓冲区的问题,内容偏向于基础一点的,学习完之后可以结合一些Linux相关的书籍再看看

目录

一、什么是缓冲区?

1.1 问题提出

1.2 缓冲区概念的引出

二、缓冲区刷新方案

2.1 刷新方案

2.2 问题解决

三、缓冲区的作用

四、用户缓冲区的管理

4.1 缓冲区的分配

4.2 缓冲区的对齐

五、性能优化与缓冲区管理

5.1 使用合适的缓冲区大小

5.2 异步 I/O 操作

六、 总结


我们讲解的重点会放在讲解什么是缓冲区上,对于缓冲区存在的作用和种类等方面上了解一下就行

一、什么是缓冲区?

1.1 问题提出

我们通过几个场景来揭露这个问题,首先我们先来看下面这串代码及其输出结果:

#include<stdio.h>
#include<string.h>
#include<unistd.h>              
int main()    
{             const char *fstr="hello fwrite\n";const char *str="hello write\n";//C语言接口printf("hello printf\n");   //stdout -> 1fprintf(stdout,"hello fprintf\n");   //stdout -> 1fwrite(fstr,strlen(fstr),1,stdout);  //fread, stdout -> 1//操作系统提供的系统接口write(1,str,strlen(str));                                                                                                                                                                                           return 0; }            

运行结果:

我们可以把这个结果输出重定向到指定文件中去

./myfile>log.txt
cat log.txt

但是如果我们在代码段的最后一行加入fork函数来创建子进程,我们就会得到一个不一样的输出结果:

#include<stdio.h>
#include<string.h>
#include<unistd.h>              
int main()    
{             const char *fstr="hello fwrite\n";const char *str="hello write\n";//C语言接口printf("hello printf\n");   //stdout -> 1fprintf(stdout,"hello fprintf\n");   //stdout -> 1fwrite(fstr,strlen(fstr),1,stdout);  //fread, stdout -> 1//操作系统提供的系统接口write(1,str,strlen(str));           fork();                                                                                                                                                                                return 0; }            

运行结果:

我们发现再次输出重定向时结果发生了很大的改变,调用C语言接口的语句被打印了两遍,而系统调用接口则只被打印一遍,而且顺序也发生了变化,调用系统接口的先被打印

1.2 缓冲区概念的引出

为什么会出现这种情况呢?在解释之前我们先来看下面这种情况:

#include<stdio.h>
#include<string.h>
#include<unistd.h>              
int main()    
{             const char *fstr="hello fwrite\n";//const char *str="hello write\n";//C语言接口printf("hello printf\n");   //stdout -> 1fprintf(stdout,"hello fprintf\n");   //stdout -> 1fwrite(fstr,strlen(fstr),1,stdout);  //fread, stdout -> 1close(1);//操作系统提供的系统接口//write(1,str,strlen(str));           //fork();                                                                                                                                                                                return 0; }            

我们只留下C语言的几个打印方式,同时在打印执行完后把1号文件关闭了

运行结果:

上面的每一条打印语句我们都通过\n来刷新缓冲区的,如果我们把\n去掉再执行一遍:

#include<stdio.h>
#include<string.h>
#include<unistd.h>              
int main()    
{             const char *fstr="hello fwrite";//const char *str="hello write\n";//C语言接口printf("hello printf");   //stdout -> 1fprintf(stdout,"hello fprintf");   //stdout -> 1fwrite(fstr,strlen(fstr),1,stdout);  //fread, stdout -> 1close(1);//操作系统提供的系统接口//write(1,str,strlen(str));           //fork();                                                                                                                                                                                return 0; }            

运行结果:

我们发现没有任何输出结果,这又是什么原因呢?
注意:是在close(1)关闭和\n去掉同时存在的情况下才有了这样的结果

但是如果我们是对系统接口执行这个操作:

执行结果:

我们发现此时有打印结果


其实原因如下:

我们对上面的内容做一个叙述:我们都知道我们写入的内容是要先存在缓冲区的,但实际上我们通过C语言的接口所写的内容,比如print、fprint等接口所写内容所存在的缓冲区并不是在内核中的,它是在内核以外的,是语言的这些接口随后调用write系统接口才将其写入内核中去的,在我们关闭1号显示器文件时,内核中写入的内容会被我们输出到显示其中,但是缓冲区中的内容我们需要先调用write接口写入内核中去,但是1号文件都被关闭了,所以自然 是不能再写入内核中去的,所以最后就不能被显示在显示器上

这其实就解释了缓冲区的概念:缓冲区是计算机存储器中的一块内存区域,用于临时存放在不同设备或进程间传输的数据

二、缓冲区刷新方案

2.1 刷新方案

在解决最一开始的问题前,我们先要知道一个知识点,那就是缓冲区的刷新方案是什么,因为缓冲区就是内核外一个特定的空间,所以它的大小是有限的,需要定时进行刷新,而且我们读取或存入的文件的大小也是有限的,所以就需要不同的刷新方案来帮助我们解决不同的场景

缓冲区刷新方式主要有以下三种:
a.无缓冲 --- 直接刷新
b.行缓冲 ---不刷新,直到碰到\n

c.全缓冲 --- 缓冲区满了,才刷新

除此之外当进程结束时也会进行刷新

一般向显示器打印时是行缓冲,向文件中打印时是全缓冲

2.2 问题解决

下面我们继续解决一下最一开始提出的问题,为什么fork()之后再向文件中打印出现那种情况

向文件中输出时实际上我们的缓冲方案是发生改变了,由往显示器上显示时的行刷新变为了全刷新,只有在缓冲区被写满或进程要结束时时我们的内容才会被刷新到内核中,而write是系统接口,它所打印的内容是直接被写在内核中的,所以先被打印了出去,而我们缓冲区的内容需要等待进程终止时被强制刷新,而我们的fork()创建了新的子进程,子进程会复制父进程的缓冲区,子进程在结束时也会将缓冲区中的内容进行刷新,所以我们C接口所写的内容就被打印了两次

三、缓冲区的作用

缓冲区的主要作用是缓解速度差异和提高系统的效率。例如,硬盘的读写速度远低于内存,网络传输速度也比本地内存慢,因此需要缓冲区来暂时存储数据,避免频繁的硬件访问造成性能瓶颈。

四、用户缓冲区的管理

在 Linux 系统中,用户缓冲区的分配通常是由程序员或操作系统自动管理的。在许多高级语言中,如 Python、Java,缓冲区的管理是透明的,程序员只需要调用相应的 I/O 操作函数即可。对于 C 语言或其他低级语言,程序员需要手动管理缓冲区。

4.1 缓冲区的分配

在 C 语言中,用户缓冲区可以通过 malloc()calloc() 等函数动态分配内存。例如:

#include <stdio.h>
#include <stdlib.h>int main() {size_t bufferSize = 1024;char *buffer = (char *)malloc(bufferSize);if (!buffer) {perror("malloc");return -1;}// 使用缓冲区snprintf(buffer, bufferSize, "Hello, buffer!");printf("%s\n", buffer);free(buffer);  // 释放内存return 0;
}

上述代码动态分配了一个大小为 1024 字节的缓冲区,并使用 snprintf() 函数将数据写入缓冲区。

4.2 缓冲区的对齐

在某些情况下,缓冲区需要满足特定的对齐要求,以便提高访问效率或满足硬件要求。例如,某些系统要求缓冲区的起始地址必须是 16 字节的倍数。为此,可以使用 posix_memalign()aligned_alloc() 等函数来分配对齐的内存。

#include <stdio.h>
#include <stdlib.h>int main() {void *buffer;size_t alignment = 16;size_t bufferSize = 1024;if (posix_memalign(&buffer, alignment, bufferSize) != 0) {perror("posix_memalign");return -1;}// 使用缓冲区snprintf(buffer, bufferSize, "Aligned buffer!");printf("%s\n", (char *)buffer);free(buffer);return 0;
}

五、性能优化与缓冲区管理

5.1 使用合适的缓冲区大小

选择合适的缓冲区大小对于 I/O 性能至关重要。过小的缓冲区会导致频繁的 I/O 操作,降低性能;过大的缓冲区会占用过多的内存资源。因此,合理地选择缓冲区的大小是性能优化的关键。

5.2 异步 I/O 操作

在处理大量 I/O 请求时,使用异步 I/O 操作可以显著提高系统的吞吐量。Linux 提供了 aio(Asynchronous I/O)接口,允许程序在进行 I/O 操作时不阻塞主线程。

六、 总结

用户缓冲区是 Linux 系统中处理 I/O 操作的重要机制。它能够有效地减少硬件访问次数,提高数据传输效率。本文主要就是对缓冲区的概念进行详细讲解,帮助大家理解缓冲区究竟是什么,在实际开发中,合理使用用户缓冲区能够显著提升程序的性能和资源利用率。

本篇笔记:


感谢各位大佬观看,创作不易,还请各位大佬点赞支持!!!

相关文章:

【Linux探索学习】第二十二弹——用户缓冲区:深入解析操作系统中数据交互时的缓冲区机制

Linux学习笔记&#xff1a; https://blog.csdn.net/2301_80220607/category_12805278.html?spm1001.2014.3001.5482 前言&#xff1a; 前面两章我们已经讲了一些文件操作和文件重定向问题&#xff0c;以及一些相关的知识点&#xff0c;比如文件在内存中的存储位置&#xff0…...

Cesium-(Primitive)-(CylinderOutlineGeometry)

CylinderOutlineGeometry 以下是 CylinderOutlineGeometry 类的构造函数属性,以表格形式展示: 属性名类型默认值描述lengthnumber圆柱体的长度。topRadiusnumber圆柱体顶部的半径。bottomRadiusnumber圆柱体底部的半径。slicesnumber128可选,圆柱体周长的边数。numberOfVert…...

【ETCD】【源码阅读】深入分析 storeTxnWrite.Put方法源码

该方法是 storeTxnWrite 类型中的核心方法&#xff0c;负责将键值对存储到数据库&#xff0c;同时处理键的元数据&#xff08;如版本、修订号、租约&#xff09;并管理租约关联。 目录 一、完整代码二、方法详解方法签名1. 计算修订号并初始化变量2. 检查键是否已存在3. 生成索…...

MySQL技术:深入理解索引与优化

MySQL是一个广泛使用的开源关系型数据库管理系统。它以其高性能、可靠性和易用性而闻名。在数据库操作中&#xff0c;查询优化是一个非常重要的环节&#xff0c;而索引是实现查询优化的关键技术之一。本文将深入探讨MySQL中的索引原理、类型以及如何优化索引以提高数据库性能。…...

【广东-东莞】《东莞市政府投资信息化项目造价指南》-省市费用标准解读系列26

2023年6月27日&#xff0c;东莞市发展和改革局发布《东莞市政府投资信息化项目造价指南&#xff08;试行&#xff09;》&#xff0c;此指南由东莞市政府投资项目评审中心编制&#xff0c;指南旨在完善东莞市为规范政府投资信息化项目造价计费方式&#xff0c;高质量、高效率推进…...

8、基于SpringBoot的房屋租赁系统

摘 要 社会的发展和科学技术的进步&#xff0c;互联网技术越来越受欢迎。网络计算机的生活方式逐渐受到广大人民群众的喜爱&#xff0c;也逐渐进入了每个用户的使用。互联网具有便利性&#xff0c;速度快&#xff0c;效率高&#xff0c;成本低等优点。 因此&#xff0c;构建符…...

SLM510A系列——24V,15到150mA单通道可调电流线性恒流LED驱动芯片

SLM510A 系列产品是单通道、高精度、可调电流线性恒流源的 LED 驱动芯片&#xff0c;在各种 LED 照明产品中非常简单易用。其在宽电压输入范围内&#xff0c;能保证极高的输出电流精度&#xff0c;从而在大面积的光源照明中&#xff0c;都能让 LED 照明亮度保持均匀一致。 由于…...

深度学习试题及答案解析(一)

1. 一幅256*256的图像&#xff0c;若灰度级数为16&#xff0c;则存储它所需的比特数是&#xff08;&#xff09; 2. 在深度学习中&#xff0c;涉及大量的矩阵相乘&#xff0c;现在需要计算三个稠密矩阵A&#xff0c;B&#xff0c;C的乘积ABC,假设三个矩阵的尺寸分别为m∗n&…...

【钉钉群聊机器人定时发送消息功能实现】

Java实现 钉钉群聊机器人定时发送消息功能 钉钉群聊准备工作钉钉发起群聊创建项目群打开钉钉群聊设置打开机器人管理选择Webhook机器人添加机器人安全设置保存Webhook地址&#xff08;重点是token&#xff09; 项目代码实现添加依赖启动类添加定时任务启动扫描编写调度任务定义…...

uni-app多环境配置动态修改

前言 这篇文章主要介绍uniapp在Hbuilderx 中&#xff0c;通过工程化&#xff0c;区分不同环境、动态修改小程序appid以及自定义条件编译&#xff0c;解决代码发布和运行时手动切换问题。 背景 当我们使用uniapp开发同一个项目发布不同的环境二级路径不同时&#xff0c;这时候…...

verilog代码连线集成工具的实践

目录 引言 代码解析 解析器的需求 数据结构 基础class 集合class&#xff1a; 界面 模块例化里界面 连线界面 连线界面示例 消息传递 引言 工作中经常需要开发很多自动化的脚本或者小工具来提升开发效率。在没有读《Cad Frameworks: Principles And Architecture》…...

【深入STL:C++容器与算法】深度解析string类的使用

文章目录 1️⃣什么是stringstring的设计以及编码问题 2️⃣string的重要接口&#x1f4ab;&#x1f4ab;一、string的初始化二、string的赋值三、string的长度四、string元素获取1. char& at(size_t pos)2. operaotr []3. front和back 五、迭代器1. 什么是迭代器2. 范围fo…...

【ChatGPT】解锁AI思维链:如何让机器像人类一样思考?

在人工智能领域&#xff0c;我们一直在追求让机器像人类一样思考。然而&#xff0c;即使是最先进的AI&#xff0c;也常常被诟病缺乏“常识”&#xff0c;难以理解复杂问题&#xff0c;更不用说像人类一样进行逻辑推理和解决问题了。最经常的表现就是遇到不会的地方&#xff0c;…...

用 Python 从零开始创建神经网络(十七):回归(Regression)

回归&#xff08;Regression&#xff09; 引言1. 线性激活&#xff08;Linear Activation&#xff09;2. 均方误差损失&#xff08;Mean Squared Error Loss&#xff09;3. 均方误差损失导数&#xff08;Mean Squared Error Loss Derivative&#xff09;4. 平均平方误差 (MSE) …...

gentoo安装Xfce桌面

一、安装Xfce 1.选择一个配置文件 具体步骤可参见笔者的另一篇博客https://blog.csdn.net/my1114/article/details/143919066&#xff0c;配置文件选择24. 2.安装Xfce (1)root #emerge --ask xfce-base/xfce4-meta 第一次启动登录后时可能还需starx来启动X11 (2)安装slim&#…...

阿尔茨海默症数据集,使用yolo,voc,coco格式对2013张原始图片进行标注,可识别轻微,中等和正常的症状

阿尔茨海默症数据集,使用yolo&#xff0c;voc&#xff0c;coco格式对2013张原始图片进行标注&#xff0c;可识别轻微&#xff0c;中等&#xff0c;严重和正常的症状 数据集分割 训练组100&#xff05; 2013图片 有效集&#xff05; 0图片 测试集&#xf…...

【物联网技术与应用】实验4:继电器实验

实验4 继电器实验 【实验介绍】 继电器是一种用于响应施加的输入信号而在两个或多个点或设备之间提供连接的设备。换句话说&#xff0c;继电器提供了控制器和设备之间的隔离&#xff0c;因为设备可以在AC和DC上工作。但是&#xff0c;他们从微控制器接收信号&#xff0c;因此…...

lvs介绍与应用

LVS介绍 LVS&#xff08;Linux Virtual Server&#xff09;是一种基于Linux操作系统的虚拟服务器技术&#xff0c;主要用于实现负载均衡和高可用性。它通过将客户端请求分发到多台后端服务器上&#xff0c;从而提高整体服务的处理能力和可靠性。lvs是基于集群的方式实现 集群…...

Group FLUX - User Usage Survey Report

文章目录 User Feedback Summary: Software Advantages and FeaturesUser Feedback Issues and Suggested Improvements1. Security Concerns:Improvement Measures: 2. System Performance and Loading Speed:Improvement Measures: 3. Data Display Issues:Improvement Measu…...

XXE靶机攻略

XXE-Lab靶场 1.随便输入账号密码 2.使用bp抓包 3.插入xxl代码&#xff0c;得到结果 xxe靶机 1.安装好靶机&#xff0c;然后输入arp-scan -l&#xff0c;查找ip 2.输入ip 3.使用御剑扫描子域名 4.输入子域名 5.输入账号密码抓包 6.插入xml代码 7.使用工具解码 8.解码完毕放入文…...

第78期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以找…...

电容Q值、损耗角、应用

电容发热的主要原因&#xff1a;纹波电压 当电容两端施加纹波电压时&#xff0c;电容承受的是变化的电压&#xff0c;由于电容内部存在寄生电阻&#xff08;ESR&#xff09;和寄生电感&#xff08;ESL&#xff09;.因此电容会有能量损耗&#xff0c;从而产生热量&#xff0c;这…...

【WRF教程第3.6期】预处理系统 WPS 详解:以4.5版本为例

预处理系统 WPS 详解&#xff1a;以4.5版本为例 Geogrid/Metgrid 插值选项详解1. 插值方法的工作机制2. 插值方法的详细说明2.1 四点双线性插值&#xff08;four_pt&#xff09;2.2 十六点重叠抛物线插值&#xff08;sixteen_pt&#xff09;2.3 简单四点平均插值&#xff08;av…...

linux 安装redis

下载地址 通过网盘分享的文件&#xff1a;redis-7.2.3.tar.gz 链接: https://pan.baidu.com/s/1KjGJB1IRIr9ehGRKBLgp4w?pwd0012 提取码: 0012 解压 tar -zxvf redis-7.2.3.tar.gz mv redis-7.2.3 /usr/local/ cd /usr/local/redis-7.2.3 安装 make install 修改配置文件 /搜索…...

Linux - rpm yum 工具及命令总结

RPM 概述 定义&#xff1a;RPM&#xff08;RedHat Package Manager&#xff09;&#xff0c;是一个功能强大的软件包管理系统&#xff0c;用于在 Linux 系统中安装、升级和管理软件包采用系统&#xff1a;主要用于基于 RPM 的 Linux 发行版&#xff0c;如 Red Hat、CentOS、S…...

电子应用设计方案-58:智能沙发系统方案设计

智能沙发系统方案设计 一、引言 智能沙发作为一种融合了舒适与科技的家居产品&#xff0c;旨在为用户提供更加便捷、舒适和个性化的体验。本方案将详细介绍智能沙发系统的设计思路和功能实现。 二、系统概述 1. 系统目标 - 实现多种舒适的姿势调节&#xff0c;满足不同用户的…...

复习打卡Linux篇

目录 1. Linux常用操作命令 2. vim编辑器 3. 用户权限 4. Linux系统信息查看 1. Linux常用操作命令 基础操作&#xff1a; 命令说明history查看历史执行命令ls查看指定目录下内容ls -a查看所有文件 包括隐藏文件ls -l ll查看文件详细信息&#xff0c;包括权限类型时间大小…...

在Ubuntu 22.04 LTS中使用PyTorch深度学习框架并调用多GPU时遇到indexSelectLargeIndex相关的断言失败【笔记】

在Ubuntu 22.04 LTS系统中&#xff0c;已安装配置好CUDA 12.4、cuDNN 9.1.1以及PyTorch环境 export CUDA_VISIBLE_DEVICES0,1,2,3,4,5,6,7 在PyTorch深度学习框架训练调用多GPU时&#xff0c;提示 indexSelectLargeIndex: block: [x, 0, 0], thread: [x, 0, 0] Assertion src…...

qt 类中的run线程

在Qt中&#xff0c;QThread类的run()方法是线程的执行入口&#xff0c;它是由QThread内部自动调用的&#xff0c;而不是用户直接调用。 详细解释&#xff1a; QThread类&#xff1a; QThread是Qt的线程类&#xff0c;提供了用于多线程操作的接口。我们可以创建QThread对象并将…...

Vue3父子组件传属性和方法调用Demo

Vue3父子组件传属性和方法调用Demo 说明目录父组件给子组件传值和方法 父组件给子组件传值-使用defineProps接受父组件属性值父组件给子组件传值-使用defineModel接受父组件v-model值 当子组件只需要接收父组件一个v-model值时,写法1如下:子组件接收单个v-model写法2如下:当子…...