一 前言
博文《PageCache》中介绍了 PageCache 的优缺点,其实在处理大文件中 PageCache 作用反而没有那么好。所以本文介绍 Linux 是怎么处理大文件的。
二 起因
首先看一下一个 read() 系统调用流程发生了什么,如下图:
- 当调用 read 方法时,会阻塞着,此时内核会向磁盘发起 I/O 请求。
- 磁盘收到请求后,便会寻址,当磁盘数据准备好后,就会向内核发起 I/O 中断,告知内核磁盘数据已经准备好。
- 内核收到 I/O 中断后,就将数据从磁盘控制器缓冲区拷贝到 PageCache 里。
- 最后,内核再把 PageCache 中的数据拷贝到用户缓冲区,于是 read 调用返回。
从上图不难发现两个问题:1,这是一个同步 I/O;2,数据多次拷贝。通常分别采用 异步 I/O 和 直接 I/O 来优化上面的问题。
三 异步 I/O、 直接 I/O
异步 I/O 是指发起 I/O 请求后,不再阻塞等待。而是先去处理其他任务,当拷贝完成后再进行处理。
异步 I/O 并没有涉及到 PageCache,所以使用异步 I/O 就意味着要绕开 PageCache。
绕开 PageCache 的 I/O 叫直接 I/O,使用 PageCache 的 I/O 则叫缓存 I/O。通常,对于磁盘,异步 I/O 只支持直接 I/O。
如下图:
- 内核向磁盘发起读请求,但是可以不等待数据就位就可以返回,于是进程此时可以处理其他任务。
- 当内核将磁盘中的数据拷贝到进程缓冲区后,进程将接收到内核的通知,再去处理数据。
四 总结
针对大文件的传输的方式,应该使用「异步 I/O + 直接 I/O」来替代零拷贝技术。常见的两种 直接 I/O 应用场景:
- 如在 MySQL 数据库中,是使用直接 I/O的。应用程序已经实现了磁盘数据的缓存,那么可以不需要 PageCache 再次缓存,减少额外的性能损耗。
- 传输大文件的时候,由于大文件难以命中 PageCache 缓存,而且会占满 PageCache 导致「热点」文件无法充分利用缓存,从而增大了性能开销,因此,这时应该使用直接 I/O。