磁盘输入输出三种方式的比较:标准输入输出、直接输入输出和混合内存存取
文章目录
@[TOC] 1。介绍
1.1标准输入/输出
1.2直接输入/输出
1.3 mmap
1.介绍
先来看看Linux系统下的IO结构模型。
从图中我们可以看到标准I/O、mmap和直接I/O的流程差异。
1.1标准输入/输出
大多数文件系统的默认I/O操作是标准I/O,在Linux的缓存I/O机制中,数据是从磁盘复制到内核空间的缓冲区,再从内核空间的缓冲区复制到应用程序的地址空间。
读取操作:操作系统检查内核的缓冲区是否有需要的数据,如果已经缓存,则直接从缓存中返回;否则,它将从磁盘中读取,然后缓存在操作系统的缓存中。
写操作:将数据从用户空间复制到内核空间的缓存中。此时,用户程序的写入操作已经完成。至于何时写入磁盘,则取决于操作系统,除非显示sync等同步命令。
缓存I/O的优点:1)一定程度上分离了内核空间和用户空间,保护了系统本身的运行安全;2)可以减少读盘次数,从而提高性能。
缓存I/O的缺点:数据传输过程中,应用地址空间和缓存之间需要进行多次数据复制操作,这些数据复制操作造成的CPU和内存开销非常大。
让我们以read()操作为例,下图显示了read操作的流程。
这将导致数据首先从磁盘复制到页面缓存,然后从页面缓存复制到应用程序的用户空间,这将导致多一个内存副本。这种系统设计主要是因为内存相对于磁盘来说是一个高速设备,即使多拷贝100倍,内存也比实际读取硬盘一次要快。
写入操作
如前所述,写操作意味着数据从用户控件复制到内核空间的缓存中。数据何时写入磁盘由应用程序采用的写入操作机制决定。默认情况下,采用延迟写入机制。应用程序只需要将数据写入页面缓存,根本不需要等待所有数据写入磁盘。系统将负责定期将页面缓存数据写入磁盘。
可以看出,缓存I/O可以大大减少实际读写磁盘的次数,从而提高性能。但是,延迟写入机制可能会导致数据丢失。系统何时会将页面缓存数据真正写入磁盘?
页面缓存中修改后的内存称为“脏页”,内核通过刷新线程定期将数据写入磁盘。具体的写入条件可以通过/proc/sys/vm文件或sysctl -a | grep vm命令获得。
在实际应用中,如果我们认为一些数据非常重要,就完全没有丢失的风险。此时,我们应该采用同步写入机制。当在应用程序中使用sync、fsync和msync等系统调用时,内核会立即将相应的数据写回磁盘。
1.2直接输入/输出
直接IO意味着应用程序直接访问磁盘数据,而不经过内核缓冲区。这样做的目的是减少从内核缓冲区到用户程序缓存的数据拷贝。例如,数据库管理系统等应用程序更倾向于选择自己的缓存机制,因为数据库管理系统往往比操作系统更了解数据库中存储的数据,而数据库管理系统可以提供更有效的缓存机制来提高数据库中数据的访问性能。
直接IO的缺点:如果访问的数据不在应用缓存中,那么每次都会直接从磁盘加载数据,非常耗时。通常,直接IO和异步IO的组合会获得更好的性能。(异步IO:当访问数据的线程发出请求时,线程会继续做其他事情,而不是阻塞等待)
直接输入输出过程
从图中可以看出,对文件的直接I/O访问减少了一次数据拷贝和一些系统调用的耗时,大大降低了CPU的利用率和内存的使用。
但是直接与磁盘交互非常耗时,只有在确定标准I/O成本非常高的情况下,才应该考虑使用直接I/O。
1.3 mmap
Mmap是指硬盘上文件的位置与进程逻辑地址空间中相同大小的区域一一对应,当内存中的一条数据要被访问时,就转换成访问文件的一条数据。这种方法的目的也是为了减少用户空间和内核空间之间的数据复制操作。当需要传输大量数据时,使用内存映射来访问文件会获得更好的效率。
当使用内存映射文件处理存储在磁盘上的文件时,不再需要对文件执行I/O操作,这意味着在处理文件时不再需要为文件申请和分配缓存,所有文件缓存操作都由系统直接管理。由于取消了将文件数据加载到内存、将数据从内存写回文件和释放内存块的步骤,内存映射文件在处理大数据文件时可以发挥非常重要的作用。
mmap的优点:
减少系统调用。我们只需要一个mmap()系统调用,后续所有的调用都像操作内存一样,没有大量的读/写系统调用。
减少数据复制。普通的read()调用,数据需要复制两次;但是,mmap只需要从磁盘拷贝一次,由于内存映射,不需要拷贝回用户空间。
可靠性高。mmap将数据写入页面缓存后,可以依靠内核线程定期写回磁盘,就像缓存I/O的延迟写机制一样,但需要提到的是,
mmap 在内核崩溃、突然断电的情况下也一样有可能引起内容丢失,当然我们也可以使用 msync来强制同步写。
从上面的图看来,我们使用 mmap 仅仅只需要一次数据拷贝。看起来 mmap 的确可以秒杀普通的文件读写,那我们为什么不全都使用 mmap 呢事实上,它也存在一些缺点:
虚拟内存增大。mmap 会导致虚拟内存增大,我们的 APK、Dex、so 都是通过 mmap 读取。而目前大部分的应用还没支持 64 位,除去内核使用的地址空间,一般我们可以使用的虚拟内存空间只有 3GB 左右。如果 mmap 一个 1GB 的文件,应用很容易会出现虚拟内存不足所导致的 OOM。
磁盘延迟。mmap 通过缺页中断向磁盘发起真正的磁盘 I/O,所以如果我们当前的问题是在于磁盘 I/O 的高延迟,那么用 mmap() 消除小小的系统调用开销是杯水车薪的。
在 Android 中可以将文件通过MemoryFile或者MappedByteBuffer映射到内存,然后进行读写,使用这种方式对于小文件和频繁读写操作的文件还是有一定优势的。我通过简单代码测试,测试结果如下。
从上面的数据看起来 mmap 好像的确跟写内存的性能差不多,但是这并不正确,因为我们并没有计算文件系统异步落盘的耗时。在低端机或者系统资源严重不足的时候,mmap 也一样会出现频繁写入磁盘,这个时候性能就会出现快速下降。
mmap 比较适合于对同一块区域频繁读写的情况,推荐也使用线程来操作。用户日志、数据上报都满足这种场景,另外需要跨进程同步的时候,mmap 也是一个不错的选择。Android 跨进程通信有自己独有的 Binder 机制,它内部也是使用 mmap 实现。
利用 mmap,Binder 在跨进程通信只需要一次数据拷贝,比传统的 Socket、管道等跨进程通信方式会少一次数据拷贝。
https://www.cnblogs.com/longchang/p/11162813.html
------------------越是喧嚣的世界,越需要宁静的思考------------------
合抱之木,生于毫末;九层之台,起于垒土;千里之行,始于足下。
积土成山,风雨兴焉;积水成渊,蛟龙生焉;积善成德,而神明自得,圣心备焉。故不积跬步,无以至千里;不积小流,无以成江海。骐骥一跃,不能十步;驽马十驾,功在不舍。锲而舍之,朽木不折;锲而不舍,金石可镂。蚓无爪牙之利,筋骨之强,上食埃土,下饮黄泉,用心一也。蟹六跪而二螯,非蛇鳝之穴无可寄托者,用心躁也。
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/79571.html