Linux 文件缓冲区:高效数据访问的幕后推手
个人主页:chian-ocean文章专栏-Linux
前言:
文件缓冲区是操作体系用来提高文件I/O操作效率的内存区域。在处置处罚文件的读写操作时,缓冲区起着重要的作用。缓冲区通常是在内存中分配的一块空间,用于临时存储即将读取或写入的数据
https://i-blog.csdnimg.cn/direct/0fb97cee39614bf5a01142afc344bb6d.png
缓冲区的条理
语言级缓冲区(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 的内部实现(简化):
typedef struct {
unsigned char *ptr; // 当前缓冲区位置
int cnt; // 剩余字符数
unsigned char *base;// 缓冲区起始地址
int flag; // 文件状态标志
int fd; // 文件描述符
int charbuf; // 单字符缓冲区
int bufsize; // 缓冲区大小
unsigned char *tmpfname; // 临时文件名
} 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的延迟。
[*]缺点:
[*]如果步伐瓦解,缓冲区中的数据可能未被写入磁盘,导致数据丢失。
示例:
[*]文件写操作、网络数据传输。
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
// 打开名为 "log.txt" 的文件,模式是写入("w")。如果文件不存在,会创建新文件。
FILE* fp = fopen("log.txt", "w");
if(fp == NULL) // 如果文件打开失败,返回错误并退出程序
{
perror("fopen"); // 打印打开文件失败的错误信息
return -1; // 返回 -1,表示程序错误退出
}
const char* msg = "hello linu\n"; // 定义要写入文件的字符串,其中的换行符在文件中显示为换行
int cnt = 5; // 定义写入的次数(5次)
// 循环 5 次,每次写入 msg 字符串到文件中,并且间隔 2 秒
while(cnt--)
{
// 将 msg 字符串写入文件,每次写入时不会自动换行,换行符是通过 msg 字符串内的 "\n" 实现的
fprintf(fp, "%s", msg);
sleep(2); // 程序暂停 2 秒,模拟延迟
}
fclose(fp); // 关闭文件,保存写入的内容
return 0; // 程序成功结束,返回 0
}
[*] 打开文件:
[*]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,文件的写入。
while true ;do cat log.txt ;echo "----------------";sleep 1;done
[*]这个脚本会每 1 秒读取一次 log.txt 文件的内容,并将内容输出到终端。输出后会表现一行分隔符 ----------------,然后停息 1 秒,再继续下一轮读取和表现。循环将会持续举行,直到用户手动制止。
https://i-blog.csdnimg.cn/direct/83ed32f4d5c04c41a931fc0c2c7f1181.png
[*] 代码在实行的时间在log.txt前面10秒中是不会表现效果的。直至历程结束。
[*] 会在文件中打印出5层循环的"hello linux"。
行缓冲(Line Buffered)
[*]形貌:在行缓冲模式下,缓冲区的数据会在遇到换行符(\n)时革新,即当输入或输出一个完备行的数据时,缓冲区会立即写入或读取数据。这种模式在需要逐行处置处罚的应用步伐中非常有用。
[*]应用场景:通常用于交互式步伐或需要实时输出的场景。例如,下令行步伐、日记记录等。
[*]优点:
[*]得当实时输出,数据能实时反映到屏幕或文件中。
[*]缺点:
[*]可能会导致频繁的I/O操作,特别是对于大数据量的处置处罚时,性能较差。
示例:
[*]打印输出、下令行交互。
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
// 打开名为 "log.txt" 的文件,模式是写入("w")。如果文件不存在,会创建新文件。
FILE* fp = fopen("log.txt", "w");
if(fp == NULL) // 如果文件打开失败,返回错误并退出程序
{
perror("fopen"); // 打印打开文件失败的错误信息
return -1; // 返回 -1,表示程序错误退出
}
// 设置文件流的缓冲方式为行缓冲,并指定缓冲区大小为 1024 字节
setvbuf(fp, NULL, _IOLBF, 1024);
const char* msg = "hello linux\n"; // 定义要写入文件的字符串,末尾带有换行符
int cnt = 5; // 定义写入的次数(5次)
// 循环 5 次,每次写入 msg 字符串到文件中,并且间隔 2 秒
while(cnt--)
{
// 将 msg 字符串写入文件,并且格式化输出。由于设置了行缓冲,遇到换行符时会刷新缓冲区
fprintf(fp, "%s\n", msg);
sleep(2); // 程序暂停 2 秒,模拟延迟
}
fclose(fp); // 关闭文件,保存写入的内容
return 0; // 程序成功结束,返回 0
}
[*]在原来的底子上修改缓冲模式”setvbuf(fp, NULL, _IOLBF, 1024)“
再次打开监控
while true ;do cat log.txt ;echo "----------------";sleep 1;done
https://i-blog.csdnimg.cn/direct/dce7684ebc1a4b578903fba1f9f85a79.png
无缓冲(Unbuffered)
[*]形貌:无缓冲模式下,每次写入操作都会立即发生,数据不会存储到缓冲区中。每次举行读取或写入操作时,都会直接与设备举行交互。这种模式用于对数据同等性有严格要求的应用步伐。
[*]应用场景:适用于需要即时写入或读取数据的场景,如日记文件、实时数据流、错误处置处罚等。
[*]优点:
[*]数据实时写入,制止了数据丢失。
[*]缺点:
[*]性能较差,因为每次I/O操作都需要举行体系调用,增加了操作延迟。
示例:
[*]错误日记记录、实时数据采集。
总结:
[*]全缓冲:适用于大批量的读写操作,减少I/O次数,提升性能。
[*]行缓冲:适用于逐行输出或交互式应用,能够保证数据的实时性。
[*]无缓冲:适用于对数据同等性有严格要求的场景,但会影响性能。
多历程下文件缓冲区
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
// 打开文件 "log.txt",如果文件存在则清空,如果不存在则创建该文件并以可写方式打开,权限为0666(所有用户可读写)
int fd = open("log.txt", O_TRUNC | O_CREAT | O_WRONLY, 0666);
const char *fstr = "hello fwrite\n";// 用于fwrite的字符串
const char *str = "hello write\n"; // 用于write的字符串
// 将标准输出(stdout)的文件描述符重定向到刚才打开的文件(fd)
dup2(fd, 1);// 1是标准输出的文件描述符,这里将标准输出重定向到fd,即“log.txt”
// 以下函数的输出将不会显示在控制台,而是写入到“log.txt”文件中。
printf("hello printf\n"); // 输出“hello printf”到log.txt
fprintf(stdout, "hello fprintf\n"); // 输出“hello fprintf”到log.txt
fwrite(fstr, strlen(fstr), 1, stdout); // 输出“hello fwrite”到log.txt
// 使用低级I/O函数write,输出字符串到log.txt
write(1, str, strlen(str)); // 输出“hello write”到log.txt
fork();// 创建一个新的进程(子进程)。子进程会继承父进程的标准输出重定向。
return 0;// 程序结束
}
分析
文件打开与 dup2 重定向:
[*]利用 open 打开文件 log.txt,并设置文件的权限。如果文件已存在,利用 O_TRUNC 将其清空;如果不存在,则创建文件。
[*]dup2(fd, 1) 将标准输出(stdout)的文件形貌符(1)重定向到刚才打开的文件 fd,如许后续的输出都将写入到文件中。
输出重定向:
[*]printf、fprintf、fwrite 和 write 等函数的输出将不再表如今控制台,而是写入到 log.txt 文件中。
历程分叉:
[*]fork() 调用会创建一个新的历程。子历程会继承父历程的文件形貌符,因此子历程的输出也会写入到 log.txt。
https://i-blog.csdnimg.cn/direct/9dae16511e6a4c7b8df82be3b9d30b55.png
[*]write是体系调用,直接在操作体系的缓冲区上直接写入。
[*]printf fprintf fwrite 是C语言的库函数调用,会写入语言级别的缓冲区。
[*]末了历程退出的时间C语言缓冲区举行革新。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]