尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
10.【Linux系统编程】缓冲区详解——库缓冲区 VS 内核缓冲区
目录1. 引言1.1 情况1printf完不使用close1.2 情况2printf完使用close1.3 用户IO操作在系统层调用方式2. 缓冲区2.1 什么是缓冲区2.2 为什么要引入缓冲区机制2.3 缓冲区工作流程2.4 刷新方式缓冲类型2.4.0 未刷新缓冲区2.4.1 fflush强制刷新缓冲区2.4.2 stderr刷新条件满足→立即刷新2.5 库函数IO和系统调用IOprintf等write等2.6 FILE结构体2.7 简单设计一下libc库1. 引言1.1 情况1printf完不使用close// stream.c#includestdio.h#includestdlib.h#includestring.h#includeunistd.h#includesys/types.h#includesys/stat.h#includefcntl.h#includecstdlibintmain(){close(1);// fd 1intfdopen(log.txt,O_CREAT|O_WRONLY|O_APPEND,0666);// 默认stdout输出,但是将1关闭了所以此时log.txt的文件描述符fd为1stdout 1// 库函数printf(fd:%d\n,fd);printf(hello world\n);printf(hello world\n);printf(hello world\n);//系统调用constchar*msghello write\n;write(fd,msg,strlen(msg));// 没敢写close// close(fd);return0;}运行查看$catlog.txt hellowritefd:1 hello world hello world hello world1.2 情况2printf完使用close// stream.c#includestdio.h#includestdlib.h#includestring.h#includeunistd.h#includesys/types.h#includesys/stat.h#includefcntl.h#includecstdlibintmain(){close(1);// fd 1intfdopen(log.txt,O_CREAT|O_WRONLY|O_APPEND,0666);// 默认stdout输出,但是将1关闭了所以此时log.txt的文件描述符fd为1stdout 1// 库函数printf(fd:%d\n,fd);printf(hello world\n);printf(hello world\n);printf(hello world\n);//系统调用constchar*msghello write\n;write(fd,msg,strlen(msg));// 没敢写closeclose(fd);return0;}运行查看$catlog.txt hellowrite1.3 用户IO操作在系统层调用方式为什么使用close之后printf的内容就不能输出到log.txt了而write始终都可以输出到log.txt呢因为C语言标准库的缓冲区的存在缓冲区的工作流程程序写入数据 → 暂存到缓冲区 → 满足刷新条件1.显式操作 / 2.缓冲类型 / 3.程序退出→ 刷新到硬件设备2. 缓冲区2.1 什么是缓冲区缓冲区是内存空间的一部分。也就是说在内存空间中预留了一定的存储空间这些存储空间用来缓冲输入或输出的数据这部分预留的空间就叫做缓冲区。缓冲区根据其对应的是输入设备还是输出设备分为输入缓冲区和输出缓冲区。缓冲区在文件部分分为用户级缓冲区库缓冲区、内核级缓冲区2.2 为什么要引入缓冲区机制读写文件时如果不会开辟对文件操作的缓冲区直接通过系统调用对磁盘进行操作(读、写等)那么每次对文件进行一次读写操作时都需要使用读写系统调用来处理此操作即需要执行一次系统调用执行一次系统调用将涉及到CPU状态的切换即从用户空间切换到内核空间实现进程上下文的切换这将损耗一定的CPU时间频繁的磁盘访问对程序的执行效率造成很大的影响。为了减少使用系统调用的次数提高效率我们就可以采用缓冲机制。比如我们从磁盘里取信息可以在磁盘文件进行操作时可以一次从文件中读出大量的数据到缓冲区中以后对这部分的访问就不需要再使用系统调用了等缓冲区的数据取完后再去磁盘中读取这样就可以减少磁盘的读写次数再加上计算机对缓冲区的操作快于对磁盘的操作故应用缓冲区可大大提高计算机的运行速度。又比如我们使用打印机打印文档由于打印机的打印速度相对较慢我们先把文档输出到打印机相应的缓冲区打印机再自行逐步打印这时我们的CPU可以处理别的事情。可以看出缓冲区就是一块内存区它用在输入输出设备和CPU之间用来缓存数据。它使得低速的输入输出设备和高速的CPU能够协调工作避免低速的输入输出设备占用CPU解放出CPU使其能够高效率工作。2.3 缓冲区工作流程程序写入数据 → 暂存到缓冲区 → 满足刷新条件缓冲类型 / 显式操作 / 程序退出 → 刷新到硬件设备。2.4 刷新方式缓冲类型程序一般有三种刷新方式用户缓冲区的刷新方式内核缓冲区的刷新有另一套机制强制刷新执行flush语句刷新条件满足即缓冲类型包括下述三种进程结束标准I/O提供了3种类型的缓冲方式。加粗即为刷新条件全缓冲这种缓冲方式要求填满整个缓冲区后才进行I/O系统调用操作。对于磁盘文件的操作通常使用全缓冲的方式访问。行缓冲在行缓冲情况下当在输入和输出中遇到换行符时只对显示器适用标准I/O库函数将会执行系统调用操作。当所操作的流涉及一个终端时例如标准输入和标准输出使用行缓冲方式。因为标准I/O库每行的缓冲区长度是固定的所以只要填满了缓冲区即使还没有遇到换行符也会执行I/O系统调用操作默认行缓冲区的大小为1024。无缓冲无缓冲输出的核心是 “绕过标准 I/O 库的缓存直接调用系统调用”。利用默认无缓冲的stderr流通过fprintf(stderr, ...)等函数用setvbuf将指定流如stdout设置为_IONBF模式直接使用系统调用如write完全不依赖标准I/O库。2.4.0 未刷新缓冲区#includestdio.h#includestring.h#includesys/types.h#includesys/stat.h#includefcntl.h#includeunistd.hintmain(){close(1);intfdopen(log.txt,O_WRONLY|O_CREAT|O_TRUNC,0666);if(fd0){perror(open);return0;}printf(hello world: %d\n,fd);close(fd);return0;}我们本来想使用重定向思维让本应该打印在显示器上的内容写到“log.txt”文件中但我们发现程序运行结束后文件中并没有被写入内容# cat log.txt空这是由于我们将1号描述符重定向到磁盘文件后缓冲区的刷新方式成为了全缓冲。而我们写入的内容并没有填满整个缓冲区导致并不会将缓冲区的内容刷新到磁盘文件中。当然也可以通过写满缓冲区来验证是否时这个原因这里我们所说的缓冲区是指语言层面的缓冲区即C的库缓冲区而使用的close是系统调用系统调用不会检测C库缓冲区是否有数据没有刷新所以直接使用close关闭文件则会导致数据没有刷新到log.txt文件中。如果我们使用C的库函数fclose那么fclose会检测库缓冲区是否有数据没有刷新那么就会将数据刷新到log.txt里面。也可以使用fflush强制刷新下缓冲区。2.4.1 fflush强制刷新缓冲区#includestdio.h#includestring.h#includesys/types.h#includesys/stat.h#includefcntl.h#includeunistd.hintmain(){close(1);intfdopen(log.txt,O_WRONLY|O_CREAT|O_TRUNC,0666);if(fd0){perror(open);return0;}printf(hello world: %d\n,fd);fflush(stdout);close(fd);return0;}还有一种解决方法刚好可以验证一下stderr是不带缓冲区的代码如下2.4.2 stderr刷新条件满足→立即刷新#includestdio.h#includestring.h#includesys/types.h#includesys/stat.h#includefcntl.h#includeunistd.hintmain(){close(2);intfdopen(log.txt,O_WRONLY|O_CREAT|O_TRUNC,0666);if(fd0){perror(open);return0;}perror(hello world);close(fd);return0;}这种方式便可以将2号文件描述符重定向至文件由于stderr没有缓冲区“hello world”不用fflush就可以写入文件2.5 库函数IO和系统调用IOprintf等write等因为IO相关函数与系统调用接口对应并且库函数封装系统调用所以本质上访问文件都是通过fd访问的。所以C库当中的FILE结构体内部必定封装了fd。来段代码在研究一下#includestdio.h#includestring.hintmain(){constchar*msg0hello printf\n;constchar*msg1hello fwrite\n;constchar*msg2hello write\n;printf(%s,msg0);fwrite(msg1,strlen(msg0),1,stdout);write(1,msg2,strlen(msg2));fork();return0;}运行出结果helloprintfhello fwrite hellowrite但如果对进程实现输出重定向呢./hello file 我们发现结果变成了hellowritehelloprintfhello fwrite helloprintfhello fwrite我们发现printf和fwrite库函数都输出了2次而write只输出了一次系统调用。为什么呢肯定和fork有关一般C库函数写入文件时是全缓冲的而写入显示器是行缓冲。printf、fwrite库函数会自带缓冲区进度条例子就可以说明当发生重定向到普通文件时数据的缓冲方式由行缓冲变成了全缓冲。而我们放在缓冲区中的数据就不会被立即刷新。但是进程退出之后会统一刷新写入文件当中。但是fork的时候父子数据会发生写时拷贝所以当你父进程准备刷新的时候子进程也就有了同样的一份数据随即产生两份数据。write没有变化说明没有所谓的缓冲。综上printf、fwrite库函数会自带缓冲区而write系统调用没有带缓冲区。另外我们这里所说的缓冲区都是用户级缓冲区。其实为了提升整机性能OS也会提供相关内核级缓冲区不过不再我们讨论范围之内。那这个缓冲区谁提供呢printf、fwrite是库函数write是系统调用库函数在系统调用的“上层” 是对系统调用的“封装”但是write没有缓冲区而printf、fwrite有足以说明该缓冲区是二次加上的又因为是C所以由C标准库提供。2.6 FILE结构体如果有兴趣可以看看FILE结构体:typedef struct _IO_FILE FILE; 在/usr/include/stdio.h// 在/usr/include/libio.hstruct_IO_FILE{int_flags;/* High-order word is _IO_MAGIC; rest is flags.*/#define_IO_file_flags_flags//缓冲区相关/* The following pointers correspond to the C streambuf protocol. *//* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */char*_IO_read_ptr;/* Current read pointer */char*_IO_read_end;/* End of get area. */char*_IO_read_base;/* Start of putbackget area. */char*_IO_write_base;/* Start of put area. */char*_IO_write_ptr;/* Current put pointer. */char*_IO_write_end;/* End of put area. */char*_IO_buf_base;/* Start of reserve area. */char*_IO_buf_end;/* End of reserve area. *//* The following fields are used to support backing up and undo. */char*_IO_save_base;/* Pointer to start of non-current get area. */char*_IO_backup_base;/* Pointer to first valid character of backup area*/char*_IO_save_end;/* Pointer to end of non-current get area. */struct_IO_marker*_markers;struct_IO_FILE*_chain;int_fileno;//封装的文件描述符#if0int_blksize;#elseint_flags2;#endif_IO_off_t _old_offset;/* This used to be _offset but its too small. */#define__HAVE_COLUMN/* temporary *//* 1column number of pbase(); 0 is unknown. */unsignedshort_cur_column;signedchar_vtable_offset;char_shortbuf[1];/* char* _save_gptr; char* _save_egptr; */_IO_lock_t*_lock;#ifdef_IO_USE_OLD_IO_FILE};2.7 简单设计一下libc库mystdio的实现
RELATED

相关推荐

Vue DevTools 完全指南:为 Vue.js 应用解锁开发者体验的调试工具链

Vue DevTools 完全指南:为 Vue.js 应用解锁开发者体验的调试工具链

【免费下载链接】devtools-next ⚙️ Devtools for debugging Vue.js applications. 项目地址: https://gitcode.com/gh_mirrors/de/devtools-next 点击查看 免费下载 Vue DevTools 是专门面向 Vue.js 应用的调试与开发工具,本文以仓库入口文档&#xf…

📅 2026/10/6 18:36:08
CCG Workflow 双模型交叉审查指南:/ccg:spec-review 命令全流程实战

CCG Workflow 双模型交叉审查指南:/ccg:spec-review 命令全流程实战

人工智能AI 应用开发工具CLIAI Agentdsh-pluginDeepSeek 【免费下载链接】ccg-workflow 多模型协作开发系统 - Claude 编排 Codex 后端 Gemini 前端,28 个命令覆盖开发全流程,一键安装零配置 项目地址: https://gitcode.com/fengshao1227/c…

📅 2026/10/6 18:36:08
USB3.0 HUB设计与调试实战:从选型到PCB Layout的完整指南

USB3.0 HUB设计与调试实战:从选型到PCB Layout的完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/6 18:31:08
MORE NEWS

更多资讯

📰

上下文模式(Context-Mode)工程实践:从设计到排查的完整指南

1. 从“上下文模式”说起:一个被低估的工程概念第一次看到“context-mode”这个词,很多人会下意识地把它归到某个具体框架的配置项里,比如某个前端库的渲染模式、某个数据库的连接模式,或者某个AI工具的对话模式。但真正在工程一线…

📰

Spring Boot整合Elasticsearch 8.3与RabbitMQ实现MySQL数据同步

简介:面向Spring Boot开发者的Elasticsearch 8.3与RabbitMQ集成实战Demo,核心解决MySQL数据库数据变化后实时同步至Elasticsearch搜索引擎的典型需求。压缩包共78个文件,约90KB,包含21个Java源码、17个XML配置、2个YML配置以及Mav…

📰

单火智能开关取电技术全解析:从Zigbee低功耗到灯具兼容

1. 为什么老房子的开关盒里,智能开关“装不上”1.1 传统机械开关零功耗,智能开关却要“全年在线”前一阵帮朋友弄老房改造,电工师傅进门看了一眼开关盒,丢下一句话:“你这盒里只有一根火线一根控制线,没有零…

📰

被拒10次后终于过审:App Store 4.3(a)重复内容审核避坑指南

凌晨两点,手机弹出一封新邮件,标题是“App Store Review - Guideline 4.3(a) - Design: Spam”。我盯着屏幕愣了几秒,这是同一个项目第10次被拒。前9次我已经换过图标、改过界面、调过文案、甚至重构过部分功能,结果还是一模一样的…

📰

React Native Native UI封装机制与Fabric架构源码解析

1. 从一次白屏排查说起:为什么非要懂Native UI的封装 如果你用过React Native,大概率遇到过启动白屏。绝大多数人第一反应是JS包加载慢、图片资源太大,或者是某个第三方库在初始化阶段卡住了。但有一个非常隐蔽、却极其常见的白屏根因&#x…

📰

以太坊交易树与收据树:MPT构建、轻节点验证与实战避坑

刚啃以太坊源码时,我最先跳过的是区块头里的receiptsRoot。txRoot好歹能猜出来是交易集合的承诺,收据树(receipt trie)是什么?交易执行完不就行了,为什么还要单独为“收据”再建一棵树?直到后来…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬