Linux 文件缓冲区:高效数据访问的幕后推手

打印 上一主题 下一主题

主题 814|帖子 814|积分 2442

个人主页:chian-ocean

文章专栏-Linux

前言:

   文件缓冲区是操作体系用来提高文件I/O操作效率的内存区域。在处置处罚文件的读写操作时,缓冲区起着重要的作用。缓冲区通常是在内存中分配的一块空间,用于临时存储即将读取或写入的数据
  

缓冲区的条理

语言级缓冲区(Language-Level Buffer)



  • 位置:用户空间,由步伐语言的标准库管理。
  • 管理:由编程语言的标准库(如C语言的stdio.h)提供。
  • 作用:步伐通过语言级缓冲区来缓存文件I/O操作,制止频繁的体系调用。语言级缓冲区的管理和利用通常对开发者透明,步伐员可以通过特定的API举行设置(如缓冲区类型、大小等)。
  • 示例

    • C语言的 stdio 缓冲区:如 fread()、fwrite()、printf() 等函数利用缓冲区来提高文件I/O的效率。数据会先存储在缓冲区,直到缓冲区满或手动革新时,才会写入磁盘。
    • 行缓冲(Line Buffered)、**全缓冲(Fully Buffered)无缓冲(Unbuffered)**是不同的缓冲策略。

  • 优点

    • 提高I/O操作的效率。
    • 步伐员可以控制缓冲区的大小和革新策略,灵活性较高。

C语言缓冲区

在C语言中,FILE* 是一个指向 FILE 布局体的指针,FILE 布局体在标准库中用于表示一个打开的文件。FILE* 用于在文件操作函数中引用文件对象,它是C语言标准库中的一个重要数据类型,用来管理文件的输入输出。
FILE 的内部实现(简化):
  1. typedef struct {
  2.     unsigned char *ptr;   // 当前缓冲区位置
  3.     int cnt;              // 剩余字符数
  4.     unsigned char *base;  // 缓冲区起始地址
  5.     int flag;             // 文件状态标志
  6.     int fd;               // 文件描述符
  7.     int charbuf;          // 单字符缓冲区
  8.     int bufsize;          // 缓冲区大小
  9.     unsigned char *tmpfname; // 临时文件名
  10. } FILE;
复制代码


  • 可以发如今FILE布局体中存在缓冲区。
  • 这是存在于语言级别的缓冲区,然后在于体系或者硬件级交互。
操作体系级缓冲区(System-Level Buffer)



  • 位置:操作体系内核中的内存区域。
  • 管理:由操作体系内核管理。
  • 作用:操作体系利用缓冲区来缓存来自文件体系的数据和元数据(如i-node、块设备缓存、页缓存等)。体系级缓冲区通常用于减少与磁盘的直接交互,优化磁盘I/O操作和文件体系性能。
  • 示例

    • 页缓存(Page Cache):操作体系将文件的数据缓存在内存中,后续对该文件的访问会直接从内存缓存中获取,制止频繁访问磁盘。
    • 块设备缓存(Block Cache):磁盘数据的块缓存,在块设备(如硬盘)和内存之间举行高效的数据传输。
    • 文件缓冲区(Buffer Cache):用于存储文件的元数据,如文件体系的超等块、目次项、i-node等。

  • 优点

    • 提高文件体系的性能,减少对磁盘的访问频率。
    • 优化I/O操作,提升体系效率。

当然还存在**硬件级缓冲区(Hardware-Level Buffer)**和 应用级缓冲区(Application-Level Buffer) 在这里面只谈上面两个。
缓冲区类型

缓冲区是盘算机中用于存储临时数据的内存区域,通常在输入输出(I/O)操作中发挥重要作用。缓冲区的类型可以根据其工作原理和利用场景的不同来分类,常见的缓冲区类型包括全缓冲行缓冲无缓冲三种。每种类型的缓冲区在处置处罚数据时的策略和应用场景不同。
全缓冲(Fully Buffered)



  • 形貌:在全缓冲模式下,数据会先存储到缓冲区中,直到缓冲区满或文件关闭时,才会一次性写入目标设备(如磁盘)。如果读取数据,缓冲区的数据会一次性读取。
  • 应用场景:适用于需要高效举行大量数据写入的场景,例如文件的读写操作。利用全缓冲可以减少磁盘I/O的次数,从而提高步伐性能。
  • 优点:

    • 减少了磁盘操作的次数,减少I/O的延迟。

  • 缺点:

    • 如果步伐瓦解,缓冲区中的数据可能未被写入磁盘,导致数据丢失。

示例


  • 文件写操作、网络数据传输。
  1. #include <stdio.h>  
  2. #include <unistd.h>   
  3. #include <string.h>   
  4. int main()   
  5. {   
  6.     // 打开名为 "log.txt" 的文件,模式是写入("w")。如果文件不存在,会创建新文件。
  7.     FILE* fp = fopen("log.txt", "w");   
  8.     if(fp == NULL)    // 如果文件打开失败,返回错误并退出程序
  9.     {   
  10.         perror("fopen");    // 打印打开文件失败的错误信息
  11.         return -1;    // 返回 -1,表示程序错误退出
  12.     }   
  13.    
  14.     const char* msg = "hello linu\n";    // 定义要写入文件的字符串,其中的换行符在文件中显示为换行
  15.    
  16.     int cnt = 5;    // 定义写入的次数(5次)
  17.     // 循环 5 次,每次写入 msg 字符串到文件中,并且间隔 2 秒
  18.     while(cnt--)   
  19.     {   
  20.         // 将 msg 字符串写入文件,每次写入时不会自动换行,换行符是通过 msg 字符串内的 "\n" 实现的
  21.         fprintf(fp, "%s", msg);   
  22.         sleep(2);    // 程序暂停 2 秒,模拟延迟
  23.     }   
  24.     fclose(fp);    // 关闭文件,保存写入的内容
  25.     return 0;    // 程序成功结束,返回 0
  26. }
复制代码


  • 打开文件

    • fopen("log.txt", "w"):以写入模式打开文件 log.txt。如果文件不存在,将会创建新文件。如果打开文件失败(例如没有写入权限),则返回 NULL,并通过 perror("fopen") 打印错误信息。
    定义字符串
       

    • const char* msg = "hello linu\n";:定义了一个字符串 msg,它的内容是 "hello linu" 和换行符(\n)。换行符会在文件中起到换行的作用。
    写入文件
       

    • 在 while(cnt--) 循环中,利用 fprintf(fp, "%s", msg); 将 msg 字符串写入文件 log.txt。由于 msg 字符串中包含了换行符,文件中的每次写入都会在 hello linu 后换行。
    停息与延迟
       

    • sleep(2):在每次写入后,步伐停息 2 秒。如许文件写入的隔断就会是 2 秒。
    关闭文件
       

    • fclose(fp);:在写入完成后关闭文件,确保所有内容被生存。

启用两个终端监控 log.txt,文件的写入。
  1. while true ;do cat log.txt ;echo "----------------";sleep 1;done
复制代码


  • 这个脚本会每 1 秒读取一次 log.txt 文件的内容,并将内容输出到终端。输出后会表现一行分隔符 ----------------,然后停息 1 秒,再继续下一轮读取和表现。循环将会持续举行,直到用户手动制止。



  • 代码在实行的时间在log.txt前面10秒中是不会表现效果的。直至历程结束。
  • 会在文件中打印出5层循环的"hello linux"。
行缓冲(Line Buffered)



  • 形貌:在行缓冲模式下,缓冲区的数据会在遇到换行符(\n)时革新,即当输入或输出一个完备行的数据时,缓冲区会立即写入或读取数据。这种模式在需要逐行处置处罚的应用步伐中非常有用。
  • 应用场景:通常用于交互式步伐或需要实时输出的场景。例如,下令行步伐、日记记录等。
  • 优点:

    • 得当实时输出,数据能实时反映到屏幕或文件中。

  • 缺点:

    • 可能会导致频繁的I/O操作,特别是对于大数据量的处置处罚时,性能较差。

示例


  • 打印输出、下令行交互。
  1. #include <stdio.h>  
  2. #include <unistd.h>   
  3. #include <string.h>   
  4. int main()   
  5. {   
  6.     // 打开名为 "log.txt" 的文件,模式是写入("w")。如果文件不存在,会创建新文件。
  7.     FILE* fp = fopen("log.txt", "w");   
  8.     if(fp == NULL)    // 如果文件打开失败,返回错误并退出程序
  9.     {   
  10.         perror("fopen");    // 打印打开文件失败的错误信息
  11.         return -1;    // 返回 -1,表示程序错误退出
  12.     }   
  13.     // 设置文件流的缓冲方式为行缓冲,并指定缓冲区大小为 1024 字节
  14.     setvbuf(fp, NULL, _IOLBF, 1024);   
  15.     const char* msg = "hello linux\n";    // 定义要写入文件的字符串,末尾带有换行符
  16.     int cnt = 5;    // 定义写入的次数(5次)
  17.     // 循环 5 次,每次写入 msg 字符串到文件中,并且间隔 2 秒
  18.     while(cnt--)   
  19.     {   
  20.         // 将 msg 字符串写入文件,并且格式化输出。由于设置了行缓冲,遇到换行符时会刷新缓冲区
  21.         fprintf(fp, "%s\n", msg);   
  22.         sleep(2);    // 程序暂停 2 秒,模拟延迟
  23.     }   
  24.     fclose(fp);    // 关闭文件,保存写入的内容
  25.     return 0;    // 程序成功结束,返回 0
  26. }
复制代码


  • 在原来的底子上修改缓冲模式”setvbuf(fp, NULL, _IOLBF, 1024)“
再次打开监控
  1. while true ;do cat log.txt ;echo "----------------";sleep 1;done
复制代码

无缓冲(Unbuffered)



  • 形貌:无缓冲模式下,每次写入操作都会立即发生,数据不会存储到缓冲区中。每次举行读取或写入操作时,都会直接与设备举行交互。这种模式用于对数据同等性有严格要求的应用步伐。
  • 应用场景:适用于需要即时写入或读取数据的场景,如日记文件、实时数据流、错误处置处罚等。
  • 优点:

    • 数据实时写入,制止了数据丢失。

  • 缺点:

    • 性能较差,因为每次I/O操作都需要举行体系调用,增加了操作延迟。

示例


  • 错误日记记录、实时数据采集。
总结:



  • 全缓冲:适用于大批量的读写操作,减少I/O次数,提升性能。
  • 行缓冲:适用于逐行输出或交互式应用,能够保证数据的实时性。
  • 无缓冲:适用于对数据同等性有严格要求的场景,但会影响性能。
多历程下文件缓冲区

  1. #include <stdio.h>           
  2. #include <unistd.h>                  
  3. #include <string.h>               
  4. #include <sys/types.h>               
  5. #include <sys/stat.h>               
  6. #include <fcntl.h>                  
  7. int main()
  8. {
  9.     // 打开文件 "log.txt",如果文件存在则清空,如果不存在则创建该文件并以可写方式打开,权限为0666(所有用户可读写)
  10.     int fd = open("log.txt", O_TRUNC | O_CREAT | O_WRONLY, 0666);
  11.    
  12.     const char *fstr = "hello fwrite\n";  // 用于fwrite的字符串
  13.     const char *str = "hello write\n";    // 用于write的字符串
  14.    
  15.     // 将标准输出(stdout)的文件描述符重定向到刚才打开的文件(fd)
  16.     dup2(fd, 1);  // 1是标准输出的文件描述符,这里将标准输出重定向到fd,即“log.txt”
  17.    
  18.     // 以下函数的输出将不会显示在控制台,而是写入到“log.txt”文件中。
  19.     printf("hello printf\n"); // 输出“hello printf”到log.txt
  20.     fprintf(stdout, "hello fprintf\n"); // 输出“hello fprintf”到log.txt
  21.     fwrite(fstr, strlen(fstr), 1, stdout); // 输出“hello fwrite”到log.txt
  22.    
  23.     // 使用低级I/O函数write,输出字符串到log.txt
  24.     write(1, str, strlen(str)); // 输出“hello write”到log.txt
  25.    
  26.     fork();  // 创建一个新的进程(子进程)。子进程会继承父进程的标准输出重定向。
  27.    
  28.     return 0;  // 程序结束
  29. }
复制代码
分析

文件打开与 dup2 重定向:


  • 利用 open 打开文件 log.txt,并设置文件的权限。如果文件已存在,利用 O_TRUNC 将其清空;如果不存在,则创建文件。
  • dup2(fd, 1) 将标准输出(stdout)的文件形貌符(1)重定向到刚才打开的文件 fd,如许后续的输出都将写入到文件中。
输出重定向:


  • printf、fprintf、fwrite 和 write 等函数的输出将不再表如今控制台,而是写入到 log.txt 文件中。
历程分叉:


  • fork() 调用会创建一个新的历程。子历程会继承父历程的文件形貌符,因此子历程的输出也会写入到 log.txt。



  • write是体系调用,直接在操作体系的缓冲区上直接写入。
  • printf fprintf fwrite 是C语言的库函数调用,会写入语言级别的缓冲区。
  • 末了历程退出的时间C语言缓冲区举行革新。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

张国伟

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表