首页 > 其他分享 >IO 缓冲区

IO 缓冲区

时间:2024-09-23 13:46:58浏览次数:1  
标签:文件 调用 stdio 内核 IO 缓冲区 数据

IO 缓冲区

认识

首先我们要明白一些概念,用户级和内核级,可以简单的理解为,我们平时的编程就是在用户级干活,而内核级就是操作系统的地盘,当我们需要操作系统帮忙的时候,就需要调用操作系统提供的一些接口,也就是系统调用(其实就是些函数),这时候就是由用户级进入到内核级执行

然后,我们还要理解一下缓冲区的概念,其实你可以理解为过渡,举个例子,有两个岛,中间是一片海,游客想从A岛到B岛,就得坐船,对船夫来说,当人都坐满的时候,在划船最划算(不然一个游客就划一个来回,都累死了),其次,如果船够大,一次能够运送的游客就越多,也更划算

ok,以上这个例子其实就是缓冲区,岛A是用户级,岛B是内核级,这个船就是缓冲区了,游客就是传递的数据,缓冲区其实就是暂存数据,等缓冲区满了在传递给内核,这样只需一次系统调用就能传递许多数据,非常划算,其次缓冲区的大小也很重要

有了这些概念后,直接看下面这个图,分为用户态和内核态(其实就是用户级和内核级)

1

ok,我们看到用户态和内核态之间是隔着 I/O系统调用 的,这个就是二者交互的接口啦(或者说分界线),现在让我们按顺序来讲讲数据是怎么传递的吧

首先,这是我们的用户数据,它经过一些C库函数(stdio库)的操作进入到 stdio缓冲区(用户态缓冲区),然后等到缓冲区满或者其他一些条件满足,经由 I/O系统调用 进入到 内核缓冲区,然后在等缓冲区满或者其他一些条件满足,经由 内核发起的写操作 写入到磁盘中,这是写操作的,读操作其实就是逆向啦

然后呢,以上是正常的步骤,就是图中实箭头的部分,虚箭头是通过其他的一些手段 加快缓冲区的刷新(即数据的传递,比如说不用等缓冲区满等等)

stdio库的缓冲

设置一个 stdio 流的缓冲模式

调用 setvbuf()函数,可以控制 stdio 库使用缓冲的形式

3

  • 参数介绍

    • FILE *stream: 文件流,即代表要操作的文件,打开流后,必须在调用任何其他 stdio 函数之前先调用 setvbuf()
    • char *buf: 缓冲区
    1. buf 不为 NULL,那么其指向 size 大小的内存块以作为 stream 的缓冲区因 为 stdio 库将要使用 buf 指向的缓冲区,所以应该以动态或静态在堆中为该缓冲区 分配一块空间(使用 malloc()或类似函数)
    2. 若 buf 为 NULL,那么 stdio 库会为 stream 自动分配一个缓冲区(除非选择非缓冲的I/O)
    • size_t size: 指定缓冲区的大小
    • int mode: 指定缓冲类型
      4

    这里我认为需要注意的是,行缓冲模式下,换行符 可以理解为缓冲的结束标志

    还有一些类似功能的函数,比如 setbuf, setbuffer,这里就不展开了

刷新 stdio 缓冲区

5

flush 的作用是将 指定文件流中的所有未写入的数据立即写入到文件中。它确保缓冲区的数据被实际输出到文件,以便用户能看到最新的内容。通常用于处理输出流(如标准输出)时,确保数据及时可用

stdin 和 stdout 的行为影响输入输出的顺序和刷新机制。若 stdin 和 stdout 指向终端,读取 stdin 时会隐式刷新 stdout,但这不包括换行符。这意味着你应该手动调用 fflush(stdout) 来确保提示信息立即显示,以提高可移植性

换句话说就是,一般我们在让用户输入时,都会有个提示符,可能使用printf或者其他的,这里的意思就是让我们在printf后接上 fflush(stdout),确保在输入前,提示信息能够立即显示,然后不包括换行符是因为 换行符会自动触发缓冲区刷新(行缓冲模式)

若打开一个流同时用于输入和输出,则 C99 标准中提出了两项要求。首先,一个输出操作不能紧跟一个输入操作,必须在二者之间调用 fflush()函数或是一个文件定位函数(fseek()、fsetpos()或者 rewind())。其次,一个输入操作不能紧跟一个输出操作,必须在二者之间调用一个文件定位函数,除非输入操作遭遇文件结尾

  • 输出后接输入:如果你先执行了输出操作,比如 printf,然后直接进行输入操作,比如 scanf,可能会出现输入读取错误。因此,必须在这两者之间调用 fflush(stdout) 或者文件定位函数,如 fseek()

  • 输入后接输出:如果你先进行了输入操作,然后立刻执行输出操作,可能会导致未定义行为。这种情况下,也需要调用文件定位函数

控制文件 I/O 的内核缓冲

  • 同步 I/O 数据完整性:强调读取输出的数据时一致的
  • 同步 I/O 文件完整性:同步I/O数据完整性+更新文件的元数据(比如修改的时间戳等等)

系统调用

  • 影响单个文件
    • int fsync(int fd): 对应同步 I/O 文件完整性
    • int fdatasync(int fd): 对应同步 I/O 数据完整性
  • 影响所有文件
    • void sync(void): 同步系统中所有的文件数据和元数据

open函数标志的扩展

  • O_SYNC:按照同步 I/O 文件完整性的要求执行写操作
  • O_DSYNC:按照同步 I/O 数据完整性的要求执行写操作
  • O_RSYNC: 与 O_SYNC 标志或 O_DSYNC 标志配合一起使用的,先执行对应的写操作在读
  • O_DIRECT: 直接 I/O,绕过缓冲区高速缓存
    6

混合使用库函数和系统调用进行文件 I/O

7

以上两个函数适用于文件流和文件描述符之间的转换的,需要注意的是mode参数要与文件描述符 fd 的访问模式一致

I/O 系统调用会直接将数据传递到内核缓冲区高速缓存,而 stdio 库函数会等
到用户空间的流缓冲区填满,再调用 write()将其传递到内核缓冲区高速缓存,通常情况下,printf()函数的输出往往在 write()函数的输出之后出现(特殊情况:fflush或者换行符刷新)

再探

虚线:左侧所示为可于任何时刻显式强制刷新各类缓冲区的调用。图右侧所示为促使刷新自动化的调用:一是通过禁用 stdio 库的缓冲,二是在文件输出类的系统调用中用同步,从而使每个 write()调用立刻刷新到磁盘

1

补充

// fp表示一个文件流
fflush(fp);
fsync(fileno(fp));

该组合用于确保文件的数据被安全地写入磁盘,具体效果如下:

  • fflush(fp);

    • 作用:将缓冲区中的数据立即写入到文件中。这意味着,任何在 fp 指向的文件流中尚未写入磁盘的数据都会被写入
    • 场景:常用于确保用户看到的数据与实际文件内容一致,尤其在输出后需要马上看到结果时
  • fsync(fileno(fp));

    • 作用:将与 fp 文件流对应的文件描述符的所有数据和元数据同步到磁盘。这是一个更底层的操作,确保数据在物理磁盘上持久化
    • 场景:用于需要极高数据安全性的场合,如数据库系统,确保数据不会因系统崩溃而丢失
  • 整体效果

    将这两个函数结合使用,可以保证:

    数据首先从用户空间的缓冲区写入到操作系统的缓冲区
    然后,再将操作系统的缓冲区中的数据同步到物理磁盘

    这可以大幅降低数据丢失的风险,特别是在进行重要的文件写入操作后

标签:文件,调用,stdio,内核,IO,缓冲区,数据
From: https://www.cnblogs.com/dylaris/p/18426811

相关文章

  • flask db upgrade出现UnicodeDecodeError: 'utf-8' codec can't decode byte 0xd6 in
    Traceback(mostrecentcalllast):File"<frozenrunpy>",line198,in_run_module_as_mainFile"<frozenrunpy>",line88,in_run_codeFile"D:\pycharm_project\rag-api\api\.venv\Scripts\flask.exe\__main__......
  • FINM 3008/6016 Portfolio Construction
    ANUResearchSchoolofFinance,ActuarialStudiesandStatisticsFINM3008/6016PortfolioConstructionAssignmentGeneralInstructionsandInformationTheassignmentinvolvesproducingareportthatoffersportfolioconstructionadviceoraclientbas......
  • 如何处理WordPress网站提示“建立数据库连接时出错”或“Error establishing a databa
    解决WordPress网站“建立数据库连接时出错”或“Errorestablishingadatabaseconnection”问题当您在浏览器中访问WordPress网站时,可能会遇到以下错误提示:“建立数据库连接时出错”“Errorestablishingadatabaseconnection”这通常表示WordPress无法正常连接到MySQL......
  • 31927 – Application Development with .NET
    Assignment-1Spring20241FacultyofEngineeringandInformationTechnologySchoolofComputerScience31927–ApplicationDevelopmentwith.NET32998-.NETApplicationDevelopmentSPRING2024ASSIGNMENT1–SPECIFICATIONDuedateMonday23September2......
  • uniapp uni-push2.0,iOS、Android的在线离线的推送
    本次是后端服务的苹果和安卓手机FCM的推送说明:ios在线的时间可以监听到服务端推送的数据ios离线是监听不到推送的数据,只能在消息栏点击后才能监听到数据android在线可以监听到服务端数据android离线同样不能监听到,点击打开后可监听android(FCM)离线需要连接外网才可收到消息......
  • clion安装配置
    1.软件安装(2024.1.5版本)Clion2024.1.4最新激活码,破解版安装教程(亲测有效~)-犬小哈教程(quanxiaoha.com)安装完成后,基础配置:2.配置工具链:......
  • iotdb单机版部署
    本章将介绍如何启动IoTDB单机实例,IoTDB单机实例包括1个ConfigNode和1个DataNode(即通常所说的1C1D)。 1.先决条件1.1获取安装包以下是iotdb的安装包官方地址https://www.apache.org/dyn/closer.cgi/iotdb/1.3.2/apache-iotdb-1.3.2-all-bin.zip 1.2网络配......
  • C++ 条件变量condition_variable的例子
    C++标准库对条件变量有两套实现:std::condition_variable 和 std::condition_variable_any 。这两个实现都包含在 <condition_variable> 头文件的声明中。两者都需要与一个互斥量一起才能工作(互斥量是为了同步);前者仅限于与 std::mutex 一起工作,而后者可以和任何满足最......
  • NodeRed+UIOTOS绝配!流程连线搭建上位机 一
    介绍NodeRed:后端低代码,用于协议接入、边缘计算采集。UIOTOS:前端零代码,用于组态绘图、UI表单、界面交互。 作为低代码的一个成功典范,NodeRed如今在IoT物联网、工业互联网、自动化等领域势不可挡,甚至引起了流程编辑的浪潮。nodered节点编辑尽管在协议对接、边缘计算上......
  • Mobaxterm 解除session创建限制
    Mobaxterm版本为20.2去除限制步骤1下载github上面的一个开源小项目gitclonehttps://github.com/flygon2018/MobaXterm-keygen.git下载下来后cdMobaXterm-keygenpython3MobaXterm-Keygen.py<UserName><Version>UserName我填的是windows的登录用户名,Version填自己安......