• 正文
  • 相关推荐
申请入驻 产业图谱

飞凌嵌入式ElfBoard-文件I/O的了解探究之I/O缓冲

11/20 09:31
314
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

前面有提到系统IO和标准IO在缓冲方面的区别,针对缓冲部分,有一些概念和操作函数需要了解。

1.3.2.1 内核缓冲 

实际上系统IO在进行文件读写时并不会直接访问磁盘设备,而是仅仅在用户空间缓冲区和内核缓冲区(kernel buffer cache)之间复制数据,而内核会将其缓冲区中的数据写入(刷新)到磁盘设备中。

系统调用与磁盘操作并不是同步的,例如write函数并不会等待数据真正写入到磁盘之后再返回。如果在此期间,其它进程调用read函数读取该文件的这几个字节数据,那么内核将自动从缓冲区中读取这几个字节数据返回给应用程序

这样的设计,目的是为了提高文件I/O的速度和效率,使得系统调用read、write的操作更为快速,不需要等待磁盘操作(将数据写入到磁盘或从磁盘读取出数据),磁盘操作通常是比较缓慢的。内核根据相应的存储算法自动判断何时写入磁盘,这一设计更为高效,减少了内核操作磁盘的次数。

1.3.2.2 主动刷新内核缓冲 

有些场景需要立刻将数据写入磁盘设备,此时就需要使用特定的函数(fsync、fdatasync、sync)去刷新内核缓冲区的方法。

1.3.2.3 标准IO缓冲

标准I/O有自己的缓冲区(stdio缓冲区),因此虽然标准I/O是在系统I/O基础上进行封装而实现,但在效率、性能上标准I/O要优于系统I/O。

标准I/O所维护的stdio缓冲是用户空间的缓冲区,当应用程序中通过标准I/O操作磁盘文件时,为了减少系统调用的次数,标准I/O函数会将用户读取或写入文件的数据缓存在stdio缓冲区,然后再一次性将stdio缓冲区中缓存的数据通过调用系统调用系统I/O写入到系统I/O的内核缓冲区。

这种机制通过减少系统调用的频率,降低了用户空间与内核空间频繁切换的开销,从而提高了文件操作的效率和性能。同时,标准I/O还免除了开发者手动管理缓冲区的复杂性,使得文件操作更加便捷高效。

1.行缓冲

例如printf默认为行缓冲:

#include <stdio.h>

#include <unistd.h>

int main()

{

printf("This is test1n");

printf("This is test2");

while (1)

sleep(1);

return 0;

}

编译运行并查看测试结果:

This is test1

printf仅打印了第一个带换行符的“This is test1”,而第二个打印并未出现。while循环的作用是防止进程结束自动刷新缓冲区。

2.无缓冲

例如设置stdio无缓冲:

#include <stdio.h>

#include <unistd.h>

int main()

{

if (setvbuf(stdout,NULL,_IONBF,0)) {         //设置stdio为无缓冲

printf("setvbuf errorn");

return -1;

}

printf("This is test1n");

printf("This is test2");

while (1)

sleep(1);

return 0;

}

编译运行并查看测试结果:

This is test1

This is test2

printf的两个打印都成功输出,并且程序并未结束。

3.全缓冲与刷新缓冲

#include <stdio.h>

#include <unistd.h>

int main()

{

char buf[9];

if (setvbuf(stdout,buf,_IOLBF,9) != 0) {

printf("setvbuf errorn");

return -1;

}

fprintf(stdout,"1");

fflush(stdout);

while (1) {

sleep(1);

fprintf(stdout,"12345");

}

return 0;

}

编译运行并查看测试结果:

1

1234512345

1234512345^C

设置缓冲区长度为9字节、全缓冲,首次打印1并刷新缓冲区,此时按回车等待后面打印,循环每次输出到buf中5个字节数据,第二次10字节时超过缓冲区长度就会打印出来。

在文件关闭、程序退出时都会自动刷新stdio缓冲区。

相关推荐