linux 文件缓存
缓存I/O
一般来说,当调用 open()
系统调用打开文件时,如果不指定 O_DIRECT
标志,那么就是使用缓存I/O来对文件进行读写操作。我们先来看看 open()
系统调用的定义:
int open(const char *pathname, int flags, ... /*, mode_t mode */ );
下面说明一下各个参数的作用:
pathname
:指定要打开的文件路径。flags
:指定打开文件的标志。mode
:可选,指定打开文件的权限。
其中 flags
参数可选值如下表:
标志 |
说明 |
---|---|
O_RDONLY |
以只读的方式打开文件 |
O_WRONLY |
以只写的方式打开文件 |
O_RDWR |
以读写的方式打开文件 |
O_CREAT |
若文件不存在,则创建该文件 |
O_EXCL |
以独占模式打开文件;若同时设置 O_EXCL 和 O_CREATE, 那么若文件已经存在,则打开操作会失败 |
O_NOCTTY |
若设置该描述符,则该文件不可以被当成终端处理 |
O_TRUNC |
截断文件,若文件存在,则删除该文件 |
O_APPEND |
若设置了该描述符,则在写文件之前,文件指针会被设置到文件的底部 |
O_NONBLOCK |
以非阻塞的方式打开文件 |
O_NELAY |
同 O_NELAY,若同时设置 O_NELAY 和 O_NONBLOCK,O_NONBLOCK 优先起作用 |
FASYNC |
若设置该描述符,则 I/O 事件通知是通过信号发出的 |
O_SYNC |
该描述符会对普通文件的写操作产生影响,若设置了该描述符,则对该文件的写操作会等到数据被写到磁盘上才算结束 |
O_DIRECT |
该描述符提供对直接 I/O 的支持 |
O_LARGEFILE |
该描述符提供对超过 2GB 大文件的支持 |
O_DIRECTORY |
该描述符表明所打开的文件必须是目录,否则打开操作失败 |
O_NOFOLLOW |
若设置该描述符,则不解析路径名尾部的符号链接 |
flags
参数用于指定打开文件的标志,比如指定 O_RDONLY
,那么就只能以只读方式对文件进行读写。这些标志都能通过 位或 (|)
操作来设置多个标志如:
O_DIRECT: 无缓冲的输入、输出。
O_DIRECT:
直接IO:Linux允许应用程序在执行磁盘IO时绕过缓冲区高速缓存,从用户空间直接将数据传递到文件或磁盘设备,称为直接IO(direct IO)或者裸IO(raw IO)。
- 用于传递数据的缓冲区,其内存边界必须对齐为块大小的整数倍
- 数据传输的开始点,即文件和设备的偏移量,必须是块大小的整数倍
- 待传递数据的长度必须是块大小的整数倍。
不遵守上述任一限制均将导致EINVAL错误
先总结一下stdio函数库和内核采用的缓冲这两级缓冲,然后用图说明两层缓冲机制和各种缓冲类型的控制机制。
- 首先,通过stdio库将用户数据传递到stdio缓冲区,该缓冲区位于用户态内存区。
- 当缓冲区填满,stdio库会调用write()系统调用,将数据传递到内核高速缓冲区,该缓冲区位于内核态内存区。
- 最终,内核发起磁盘操作。
输入输出数据的缓冲由内核和stdio库完成。有时可能希望阻止缓冲,但这需要了解其对应用程序性能的影响。
问题:
在InnoDB存储引擎的配置中参数innodb_flush_method通常设置为O_DIRECT,这也是官方文档所推荐的设置值。DBA或开发人员知道该参数是文件打开的一个标识,启用后文件的写入将绕过操作系统缓存,直接写文件。其在InnoDB存储引擎中的表现为对于写入到数据表空间将绕过操作系统缓存。这样设置通常不会有更好的性能,但是数据库已经有自己的缓存系统,这样的设置可以确定数据库系统对于内存的使用。下面是man手册中对于O_DIRECT选项的说明:
然而在源码内,细心的读者可能会产生一些困惑,因为不管参数 innodb_flush_method的设置为何值,在刷新脏页时都会调用fsync操作,具体见函数buf_flush_buffered_writes。那么,当用户已经打开文件操作的O_DIRECT标识,为什么还需要进行一次fsync操作确保文件的写入呢?
inode中的元数据是保存在inode cache中,inode的保存文件的数据是保存在操作系统缓存中(若未开启O_DIRECT标识)
fsync操作会同步上图中的Inode cache,Buffer cache(也就是操作系统缓存),Directory cache。因此这就是为什么InnoDB存储引擎即使在文件打开时加上O_DIRECT标识,刷新脏页依然需要fsync操作。这是因为O_DIRECT标识只是忽略了图中Buffe cache。刷新文件的另一个函数fdatasync,其仅刷新buffer cache的内容到磁盘,因此比fsync有更好的性能,但是存在数据丢失的风险。
buffer 与cache:
通过这个文档,我们可以看到:
- Buffers 是对原始磁盘块的临时存储,也就是用来缓存磁盘的数据,通常不会特别大(20MB 左右)。这样,内核就可以把分散的写集中起来,统一优化磁盘的写入,比如可以把多次小的写合并成单次大的写等等。
- Cached 是从磁盘读取文件的页缓存,也就是用来缓存从文件读取的数据。这样,下次访问这些文件数据时,就可以直接从内存中快速获取,而不需要再次访问缓慢的磁盘。
- SReclaimable 是 Slab 的一部分。Slab 包括两部分,其中的可回收部分,用 SReclaimable 记录;而不可回收部分,用 SUnreclaim 记录。
好了,我们终于找到了这三个指标的详细定义。到这里,你是不是长舒一口气,满意地想着,总算弄明白 Buffer 和 Cache 了。不过,知道这个定义就真的理解了吗?这里我给你提了两个问题,你先想想能不能回答出来。
第一个问题,Buffer 的文档没有提到这是磁盘读数据还是写数据的缓存,而在很多网络搜索的结果中都会提到 Buffer 只是对将要写入磁盘数据的缓存。那反过来说,它会不会也缓存从磁盘中读取的数据呢?
第二个问题,文档中提到,Cache 是对从文件读取数据的缓存,那么它是不是也会缓存写文件的数据呢?
为了解答这两个问题,接下来,我将用几个案例来展示, Buffer 和 Cache 在不同场景下的使用情况。
然后运行dd命令向磁盘分区/dev/sdb1写入2G数据
1$ dd if=/dev/urandom of=/dev/sdb1 bs=1M count=2048
1procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- 2 r b swpd free buff cache si so bi bo in cs us sy id wa st 31 0 0 7584780 153592 97436 0 0 684 0 31 423 1 48 50 2 0 4 1 0 0 7418580 315384 101668 0 0 0 0 32 144 0 50 50 0 0 5 1 0 0 7253664 475844 106208 0 0 0 0 20 137 0 50 50 0 0 6 1 0 0 7093352 631800 110520 0 0 0 0 23 223 0 50 50 0 0 7 1 1 0 6930056 790520 114980 0 0 0 12804 23 168 0 50 42 9 0 8 1 0 0 6757204 949240 119396 0 0 0 183804 24 191 0 53 26 21 0 9 1 1 0 6591516 1107960 123840 0 0 0 77316 22 232 0 52 16 33 0
从这里你会看到,虽然同是写数据,写磁盘跟写文件的现象还是不同的。写磁盘时(也就是 bo 大于 0 时),Buffer 和 Cache 都在增长,但显然 Buffer 的增长快得多。
这说明,写磁盘用到了大量的 Buffer,这跟我们在文档中查到的定义是一样的
观察 vmstat 的输出,你会发现读磁盘时(也就是 bi 大于 0 时),Buffer 和 Cache 都在增长,但显然 Buffer 的增长快很多。这说明读磁盘时,数据缓存到了 Buffer 中。
当然,我想,经过上一个场景中两个案例的分析,你自己也可以对比得出这个结论:读文件时数据会缓存到 Cache 中,而读磁盘时数据会缓存到 Buffer 中。
到这里你应该发现了,虽然文档提供了对 Buffer 和 Cache 的说明,但是仍不能覆盖到所有的细节。比如说,今天我们了解到的这两点:
Buffer 既可以用作“将要写入磁盘数据的缓存”,也可以用作“从磁盘读取数据的缓存”。
Cache 既可以用作“从文件读取数据的页缓存”,也可以用作“写文件的页缓存”。
这样,我们就回答了案例开始前的两个问题。
参考:
https://www.cnblogs.com/suzhou/p/5381738.html
https://blog.csdn.net/shaochenshuo/article/details/72963122
https://cloud.tencent.com/developer/article/1553680
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具