You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
|
|
|
---
|
|
|
|
性能优化之 I/O 优化
|
|
|
|
---
|
|
|
|
|
|
|
|
#### 目录
|
|
|
|
|
|
|
|
1. 思维导图
|
|
|
|
2. I/O 基本知识
|
|
|
|
- 文件系统
|
|
|
|
- 磁盘
|
|
|
|
3. Android I/O
|
|
|
|
- Android 闪存
|
|
|
|
- 两个疑问
|
|
|
|
- 文件为什么会损坏?
|
|
|
|
- I/O 有时候为什么会突然很慢?
|
|
|
|
4. I/O 性能评估
|
|
|
|
- 性能指标
|
|
|
|
- I/O 测量
|
|
|
|
- 使用 proc
|
|
|
|
- 使用 starce
|
|
|
|
- 使用 vmstat
|
|
|
|
5. 总结
|
|
|
|
6. 参考
|
|
|
|
|
|
|
|
#### 思维导图
|
|
|
|
|
|
|
|
#### I/O 基础知识
|
|
|
|
|
|
|
|
说到 I/O 优化,就能想到不能再主线程读写大文件。但是 I/O 优化肯定不仅仅限于此,应用程序调用 read()、write() 方法,内核和硬件会做什么样的处理呢?整个流程又是怎么样的呢。
|
|
|
|
|
|
|
|
![](https://github.com/Omooo/Android-Notes/blob/master/images/Android/I%5CO%20%E6%B5%81%E7%A8%8B.png)
|
|
|
|
|
|
|
|
整个文件 I/O 操作由应用程序、文件系统和磁盘共同完成。首先应用程序将 I/O 命令发送给文件系统,然后文件系统会在合适的时机把 I/O 操作发给磁盘。
|
|
|
|
|
|
|
|
CPU 和内存相比磁盘是高速设备,整个流程的瓶颈在于磁盘 I/O 的性能,所以很多时候,文件系统性能相比磁盘性能更加重要,为了降低磁盘对应用程序的影响,文件系统需要各种各样的手段进行优化。
|
|
|
|
|
|
|
|
##### 文件系统
|
|
|
|
|
|
|
|
文件系统,简单来说就是存储和组织数据的方式。对于 Android 来说,现在普遍使用的是 Linux 常用的 ext4 文件系统,但是随着 Google 对 F2FS 的投入,F2FS 文件系统应该是未来 Android 的主流文件系统。
|
|
|
|
|
|
|
|
应用程序调用 read() 方法,系统会通过中断从用户空间进入内核处理流程,然后经过 VFS(虚拟文件系统)、具体文件系统、页缓存 Page Cache 。下面是 Linux 一个通用的 I/O 架构模型:
|
|
|
|
|
|
|
|
![](https://github.com/Omooo/Android-Notes/blob/master/images/Linux%20I/O%20%E6%A8%A1%E5%9E%8B.png)
|
|
|
|
|
|
|
|
1. 虚拟文件系统(VFS)
|
|
|
|
|
|
|
|
主要用于实现屏蔽具体的文件系统,为应用程序的操作提供一个统一的接口。
|
|
|
|
|
|
|
|
2. 文件系统(FS)
|
|
|
|
|
|
|
|
ext4、F2FS 都是具体的文件系统实现,文件元数据如何组织、目录和索引结构如何设计、怎么分配和清理数据,这些都是设计一个文件系统所必需考虑的。每个文件系统都有自己适合自己的应用场景,不能说 F2FS 一定比 ext4 要好。F2FS 在连续读取大文件上并没有优势,而且会占用更大的空间。
|
|
|
|
|
|
|
|
3. 页缓存(Page Cache)
|
|
|
|
|
|
|
|
在读文件的时候,先看它是不是已经在 Page Cache 中,如果命中就不会读区磁盘。在此之后,Buffer Cache 也被合并到了 Page Cache 中的 Buffer Page 了。具体来说,Page Cache 就像我们经常使用的数据缓存,是文件系统对数据的缓存,目的是提高内存命中率。Buffer Cache 就像我们经常使用的 BufferInputStream,是磁盘对数据的缓存,目的是合并部分文件系统的 I/O 请求,降低磁盘 I/O 次数。需要注意的是,它们既会用在读请求中,也会用到写请求中。
|
|
|
|
|
|
|
|
通过 /proc/meminfo 文件可以查看缓存的内存占用情况,当手机内存不足的时候,系统会回收它们的内存,这样整体 I/O 的性能就会有所降低。
|
|
|
|
|
|
|
|
```
|
|
|
|
MemTotal: 2866492 kB
|
|
|
|
MemFree: 72192 kB
|
|
|
|
Buffers: 62708 kB // Buffer Cache
|
|
|
|
Cached: 652904 kB // Page Cache
|
|
|
|
```
|
|
|
|
|
|
|
|
##### 磁盘
|
|
|
|
|
|
|
|
磁盘指的是系统的存储设备,如果应用程序要 read() 的数据没有在页缓存中,这时候就需要真正向磁盘发起 I/O 请求了。这个过程要先经过内核的通用块层、I/O 调度层、设备驱动层,最后才会交给具体的硬件设备处理。
|
|
|
|
|
|
|
|
![](https://github.com/Omooo/Android-Notes/blob/master/images/I/O%20%E8%AF%B7%E6%B1%82%E6%B5%81%E7%A8%8B.png)
|
|
|
|
|
|
|
|
1. 通用块层
|
|
|
|
|
|
|
|
系统中能够随机访问固体大小数据块的设备成为块设备,硬盘、SSD 这些都属于块设备。通用块层主要作用是接收上层发出的磁盘请求,并最终发出 I/O 请求。
|
|
|
|
|
|
|
|
2. I/O 调度层
|
|
|
|
|
|
|
|
我们不能接收到磁盘请求就立即提交到驱动层,所以增加了 I/O 调度层,它会根据设置的调度算法对请求合并和排序。
|
|
|
|
|
|
|
|
3. 块设备驱动层
|
|
|
|
|
|
|
|
块设备驱动层根据具体的物理设备,选择对应的驱动程序通过操控硬件设备完成最终的 I/O 请求。例如光盘是靠激光在表面烧录存储,闪存是靠电子擦写存储数据。
|
|
|
|
|
|
|
|
#### Android I/O
|
|
|
|
|
|
|
|
##### Android 闪存
|
|
|
|
|
|
|
|
#### I/O 监控
|
|
|
|
|