按关键词阅读:
PageCache 中的大文件数据 , 由于没有享受到缓存带来的好处 , 但却耗费 DMA 多拷贝到 PageCache 一次;
所以 , 针对大文件的传输 , 不应该使用 PageCache , 也就是说不应该使用零拷贝技术 , 因为可能由于 PageCache 被大文件占据 , 而导致「热点」小文件无法利用到 PageCache , 这样在高并发的环境下 , 会带来严重的性能问题 。
大文件传输用什么方式实现?
那针对大文件的传输 , 我们应该使用什么方式呢?
我们先来看看最初的例子 , 当调用 read 方法读取文件时 , 进程实际上会阻塞在 read 方法调用 , 因为要等待磁盘数据的返回 , 如下图:
本文图片
具体过程:
当调用 read 方法时 , 会阻塞着 , 此时内核会向磁盘发起 I/O 请求 , 磁盘收到请求后 , 便会寻址 , 当磁盘数据准备好后 , 就会向内核发起 I/O 中断 , 告知内核磁盘数据已经准备好;
内核收到 I/O 中断后 , 就将数据从磁盘控制器缓冲区拷贝到 PageCache 里;
最后 , 内核再把 PageCache 中的数据拷贝到用户缓冲区 , 于是 read 调用就正常返回了 。
对于阻塞的问题 , 可以用异步 I/O 来解决 , 它工作方式如下图:
本文图片
它把读操作分为两部分:
前半部分 , 内核向磁盘发起读请求 , 但是可以不等待数据就位就可以返回 , 于是进程此时可以处理其他任务;
后半部分 , 当内核将磁盘中的数据拷贝到进程缓冲区后 , 进程将接收到内核的通知 , 再去处理数据;
而且 , 我们可以发现 , 异步 I/O 并没有涉及到 PageCache , 所以使用异步 I/O 就意味着要绕开 PageCache 。
绕开 PageCache 的 I/O 叫直接 I/O , 使用 PageCache 的 I/O 则叫缓存 I/O 。 通常 , 对于磁盘 , 异步 I/O 只支持直接 I/O 。
前面也提到 , 大文件的传输不应该使用 PageCache , 因为可能由于 PageCache 被大文件占据 , 而导致「热点」小文件无法利用到 PageCache 。
于是 , 在高并发的场景下 , 针对大文件的传输的方式 , 应该使用「异步 I/O + 直接 I/O」来替代零拷贝技术 。
直接 I/O 应用场景常见的两种:
【coding|原来 8 张图,就可以搞懂“零拷贝”了!】应用程序已经实现了磁盘数据的缓存 , 那么可以不需要 PageCache 再次缓存 , 减少额外的性能损耗 。 在 MySQL 数据库中 , 可以通过参数设置开启直接 I/O , 默认是不开启;
传输大文件的时候 , 由于大文件难以命中 PageCache 缓存 , 而且会占满 PageCache 导致「热点」文件无法充分利用缓存 , 从而增大了性能开销 , 因此 , 这时应该使用直接 I/O 。
另外 , 由于直接 I/O 绕过了 PageCache , 就无法享受内核的这两点的优化:
内核的 I/O 调度算法会缓存尽可能多的 I/O 请求在 PageCache 中 , 最后「合并」成一个更大的 I/O 请求再发给磁盘 , 这样做是为了减少磁盘的寻址操作;
内核也会「预读」后续的 I/O 请求放在 PageCache 中 , 一样是为了减少对磁盘的操作;
于是 , 传输大文件的时候 , 使用「异步 I/O + 直接 I/O」了 , 就可以无阻塞地读取文件了 。
所以 , 传输文件的时候 , 我们要根据文件的大小来使用不同的方式:
传输大文件的时候 , 使用「异步 I/O + 直接 I/O」;
传输小文件的时候 , 则使用「零拷贝技术」;
在 Nginx 中 , 我们可以用如下配置 , 来根据文件的大小来使用不同的方式:
当文件大小大于directio值后 , 使用「异步 I/O + 直接 I/O」 , 否则使用「零拷贝技术」 。
稿源:(CSDN)
【】网址:http://www.shadafang.com/c/hn10049531C2020.html
标题:coding|原来 8 张图,就可以搞懂“零拷贝”了!( 五 )