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

Linux系统之----文件及缓冲区

1.理解Linux下一切皆是文件

1.1 基本含义

在 Linux 系统中,“一切皆是文件”是一种重要的设计理念。文件不仅是传统意义上的磁盘上的文件,还包括设备、管道、套接字等。不仅如此,像键盘、显示器、网卡、鼠标、磁盘等硬件设备都被当作文件来处理。

1.2 文件操作函数的统一性

假设我们打开了一系列文件或者进程,此时我们就要进入我们的task_struct里面,有个参数叫*files,而这个*files就包含了一系列结构体,如files_struct,里面会有文件标识符,同时由于我们的键盘,鼠标等等都可以被视作文件,那自然就有一个结构体来存储内容和属性,因为文件就是由内容和属性所构成的,因此,倘若我要读取我的键盘文件,那我就调用read函数,要写就调用write函数,对于鼠标文件,显示器文件也是同理,因此,在系统中,还有一个struct file_operations 结构,它包含了文件操作的各种函数指针,如 read、write、open、release 等。对于不同的文件类型(包括设备文件),只要实现了对应的文件操作函数,就可以通过统一的接口进行操作。

但是有一点要注意的是,不同的设备有不同的访问方式,例如,键盘、鼠标等输入设备可以被看作是只读的文件,因为它们主要是向系统提供输入;而显示器可以看作是只写的文件,因为它主要是接收系统输出的内容。对于磁盘等存储设备,既可以读也可以写。对这些设备的操作(如从键盘读取输入、向显示器写输出、读写磁盘等)都是通过文件操作函数(如 read、write 等)来实现的。

2.缓冲区

 2.1 什么是缓冲区

缓冲区本质上是一块内存区域,用于临时存放数据。它在数据从一个地方传输到另一个地方时起到暂时存储的作用。例如,在文件读写操作中,数据通常会先从磁盘读取到缓冲区,然后再从缓冲区拷贝到用户空间的进程内存中;或者从用户空间的进程内存拷贝到缓冲区,最后写入磁盘。

2.2 为什么要有这个缓冲区

1. 减少系统调用次数

每次进行系统调用时,CPU需要从用户态切换到内核态,这涉及到保存当前进程的状态、切换到内核模式、执行系统调用相关的代码,然后再切换回用户态。这些操作需要消耗额外的CPU时间。

而通过引入缓冲区,在磁盘文件操作时,系统可以一次性从磁盘读取大量的数据到缓冲区中。后续对这部分数据的访问就可以直接在缓冲区中进行,无需再次进行系统调用。这样大大减少了系统调用的次数,从而减少了CPU时间的消耗。

举例:假设一个程序需要频繁地读取小块数据,如果没有缓冲区,每次读取都需要进行系统调用。而有了缓冲区,程序可以一次性读取一大块数据到缓冲区,之后的多次读取操作都在缓冲区中进行,大大提高了效率。

2. 提高I/O操作效率

磁盘读写效率低:磁盘的读写速度远低于内存的读写速度。如果每次对文件进行读写操作都直接访问磁盘,频繁的磁盘I/O操作会严重影响程序的执行效率。

缓冲区的高效性:计算机对缓冲区(内存区域)的操作速度远远快于对磁盘的操作速度。通过在内存中设置缓冲区,数据在内存中进行缓存,使得对数据的访问速度大幅提高。

数据合并与批量处理:缓冲区机制可以将多次小的读写操作合并为一次大的读写操作。例如,当多个进程需要写入少量数据时,这些数据可以先存储在缓冲区中,等到缓冲区填满后再一次性写入磁盘,减少了磁盘的读写次数,提高了整体的I/O效率。

2.3 三种类型的缓冲区

2.3.1 全缓冲

特点 :当使用全缓冲时,标准 I/O 库会为文件关联一个缓冲区,通常是固定大小的块。输出操作会先将数据写入缓冲区,当缓冲区被填满时,才会将缓冲区中的数据一次性写入文件;输入操作则是先从文件中读取一块数据到缓冲区,之后从缓冲区中逐个读取数据,当缓冲区中的数据被读取完毕后,才会再次从文件中读取数据填充缓冲区。

巧记:可以类比为发快递,这一车满了才能发货,等到了收货地之后,车上的货全卸下来之后才能开走拉下一批货,我这个车就可以类比为全缓冲区

适用场景 :一般用于文件读写操作,特别是对于非终端设备的文件访问,如磁盘文件等。因为对于这类文件,数据的读写通常是以块为单位进行的,使用全缓冲可以减少对文件的频繁读写操作,提高 I/O 效率。例如,当我们打开一个磁盘文件进行读写时,如果使用全缓冲模式,每次写入数据时会先写入缓冲区,当缓冲区满时才会真正写入磁盘文件,这样可以减少磁盘 I/O 次数,提高写入速度。

2.3.2 行缓冲

特点 :行缓冲也是使用缓冲区来存储数据,但它的缓冲区大小通常和一行文本的长度相关。输出时,当遇到换行符时,缓冲区中的数据会被立即写入文件;输入时,从文件中读取数据到缓冲区,直到读取到换行符为止。

适用场景 :主要用于终端输入输出操作,如标准输入(stdin)、标准输出(stdout)等。因为终端的交互通常是逐行进行的,使用行缓冲可以更好地匹配用户的输入输出习惯,提高交互的效率和便捷性。例如,在命令行中输入命令时,只有当我们按下回车键(换行符)后,输入的命令才会被发送给程序进行处理,这就是行缓冲的体现。

2.2.3 不带缓冲

特点 :不带缓冲的 I/O 操作没有缓冲区,所有的读写操作都是直接对文件进行的。每次调用读写函数都会直接与文件进行数据交换,没有中间的缓冲环节。

适用场景 :用于对时间要求严格非常的操作,或者需要直接控制文件读写的情况。由于没有缓冲区的开销,不带缓冲的 I/O 操作能够提供更精确的读写控制和更低的延迟。例如,在一些对实时性要求极高的嵌入式系统中,或者在直接操作硬件设备时,可能会使用不带缓冲的 I/O 操作来确保数据的及时传输和处理。标准出错流stderr通常是不带缓冲区的,这使得出错信息能够尽快地显示出来。

除了上述默认方式外,还有那些刷新的方式呢?

2.2.4 特殊方式

第一个就是缓冲区满时,这个不用过多解释

第二个就是执行flush语句时;

我们看如下代码:

# include <stdio.h>
# include <string.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <unistd.h>
int main() {close(1);int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);if (fd < 0) {perror("open");return 0;}printf("hello world: %d\n", fd);close(fd);return 0;
}

执行上述代码后,我们发现并没有刷新出我们的想要的内容!

原因就是我们将1号描述符重定向到磁盘文件后,缓冲区的刷新方式成为了全缓冲。而我们写入的内容并没有填满整个缓冲区,导致并不会将缓冲区的内容刷新到磁盘文件中。怎么办呢?可以使用fflush强制刷新下缓冲区。
代码如下:
# include <stdio.h>
# include <string.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <unistd.h>
int main() {close(1);int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);if (fd < 0) {perror("open");return 0;}printf("hello world: %d\n", fd);fflush(stdout);close(fd);return 0;
}

结果符合预期:

 

同理方法,可以验证一下stderr没有缓冲区! 

相关文章:

  • 【八股战神篇】Spring高频面试题汇总
  • 对单调栈的理解
  • 4082B信号/频谱分析仪
  • 视频监控联网系统GB28181协议中设备控制流程详解
  • 【Linux 性能采集】定周期采集某进程的CPU、内存、文件描述符信息并存储到文件【2025.05.21】
  • PromptIDE:一款强大的AI提示词优化工具
  • 【IC验证】systemverilog_包
  • 在线免费图片处理工具-传道软件图片工具
  • PH热榜 | 2025-05-21
  • 《对话记忆的进化史:智能体大模型如何实现跨轮次的深度交互》
  • id分页遍历数据漏行问题
  • 8天Python从入门到精通【itheima】-26~28
  • sqli-labs第十九关——POST-referer注入
  • 程序员日志之DNF手游笔者成长记录(一周年)
  • 【音频编码格式】AAC详解
  • 基于vue框架的动漫论坛g2392(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • java加强 -网络编程
  • 自动获取新版本 js 静态文件
  • 嵌入式开发学习日志(linux系统编程--文件读写函数(2))Day25
  • 【5.19-5.26学习周报】
  • 网站建设字图/免费个人网站建设
  • 邯郸网站设计招聘网/官方百度app下载安装
  • 网站建设教程答允苏州久远网络/抖音关键词排名查询