Android性能优化--IO 优化( IO基本知识:应用程序、文件系统和磁盘,三种IO方式及适用场景,多线程阻塞IO和NIO)

目录

I/O 的基本知识

1. 文件系统

2. 磁盘

Android I/O

1. Android 闪存

2. 两个疑问

疑问一:文件为什么会损坏?

疑问二:I/O 有时候为什么会突然很慢?

不同的场景使用不同的I/O方式

I/O 的三种方式

1. 标准 I/O

2. 直接 I/O

3. mmap

多线程阻塞 I/O 和 NIO

1. 多线程阻塞 I/O

2. NIO

小文件系统

常见不良现象

1. 主线程 I/O

2. 读写 Buffer 过小

3. 重复读

4. 资源泄漏

IO优化的一些思考


I/O 的基本知识

在工作中,我发现很多工程师对 I/O 的认识其实比较模糊,认为 I/O 就是应用程序执行 read()、write() 这样的一些操作,并不清楚这些操作背后的整个流程是怎样的。

这里有一张简图,可以看到整个文件 I/O 操作由应用程序、文件系统和磁盘共同完成。首先应用程序将 I/O 命令发送给文件系统,然后文件系统会在合适的时机把 I/O 操作发给磁盘。这就好比 CPU、内存、磁盘三个小伙伴一起完成接力跑,最终跑完的时间很大程度上取决于最慢的小伙伴。我们知道,CPU 和内存相比磁盘是高速设备,整个流程的瓶颈在于磁盘 I/O 的性能。所以很多时候,文件系统性能比磁盘性能更加重要,为了降低磁盘对应用程序的影响,文件系统需要通过各种各样的手段进行优化。那么接下来,我们首先来看文件系统。

1. 文件系统

文件系统,简单来说就是存储和组织数据的方式。比如在 iOS 10.3 系统以后,苹果使用 APFS(Apple File System)替代之前旧的文件系统 HFS+。对于 Android 来说,现在普遍使用的是 Linux 常用的 ext4 文件系统。

关于文件系统还需要多说两句,华为在 EMUI 5.0 以后就使用 F2FS 取代 ext4,Google 也在 Pixel 3 使用了 F2FS 文件系统。Flash-Friendly File System 是三星是专门为 NAND 闪存芯片开发的文件系统,也做了大量针对闪存的优化。根据华为的测试数据,F2FS 文件系统在小文件的随机读写方面比 ext4 更快,例如随机写可以优化 60%,不足之处在于可靠性方面出现过一些问题。随着 Google、华为的投入和规模化使用,F2FS 系统应该是未来 Android 的主流文件系统。

应用程序调用 read() 方法,系统会中断从用户空间进入内核处理流程,然后经过 VFS(Virtual File System,虚拟文件系统)、具体文件系统、页缓存 Page Cache。下面是 Linux 一个通用的 I/O 架构模型。

  • 虚拟文件系统(VFS)。它主要用于实现屏蔽具体的文件系统,为应用程序的操作提供一个统一的接口。这样保证就算厂商把文件系统从 ext4 切换到 F2FS,应用程序也不用做任何修改。
  • 文件系统(File System)。ext4、F2FS 都是具体文件系统实现,文件元数据如何组织、目录和索引结构如何设计、怎么分配和清理数据,这些都是设计一个文件系统必须要考虑的。每个文件系统都有适合自己的应用场景,我们不能说 F2FS 就一定比 ext4 要好。F2FS 在连续读取大文件上并没有优势,而且会占用更大的空间。只是对一般应用程序来说,随机 I/O 会更加频繁,特别是在启动的场景。你可以在 /proc/filesystems 看到系统可以识别的所有文件系统的列表。
  • 页缓存(Page Cache)。在读文件的时候,会先看它是不是已经在 Page Cache 中,如果命中就不会去读取磁盘。在 Linux 2.4.10 之前还有一个单独的 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

2. 磁盘

磁盘指的是系统的存储设备,就像小时候我们常听的 CD 或者电脑使用的机械硬盘,当然还有现在比较流行的 SSD 固态硬盘。

正如我上面所说,如果发现应用程序要 read() 的数据没有在页缓存中,这时候就需要真正向磁盘发起 I/O 请求。这个过程要先经过内核的通用块层、I/O 调度层、设备驱动层,最后才会交给具体的硬件设备处理。

  • 通用块层。系统中能够随机访问固定大小数据块(block)的设备称为块设备,CD、硬盘和 SSD 这些都属于块设备。通用块层主要作用是接收上层发出的磁盘请求,并最终发出 I/O 请求。它跟 VFS 的作用类似,让上层不需要关心底层硬件设备的具体实现。
  • I/O 调度层。磁盘 I/O 那么慢,为了降低真正的磁盘 I/O,我们不能接收到磁盘请求就立刻交给驱动层处理。所以我们增加了 I/O 调度层,它会根据设置的调度算法对请求合并和排序。这里比较关键的参数有两个,一个是队列长度,一个是具体的调度算法。我们可以通过下面的文件可以查看对应块设备的队列长度和使用的调度算法。

/sys/block/[disk]/queue/nr_requests      // 队列长度,一般是 128。
/sys/block/[disk]/queue/scheduler        // 调度算法

  • 块设备驱动层。块设备驱动层根据具体的物理设备,选择对应的驱动程序通过操控硬件设备完成最终的 I/O 请求。例如光盘是靠激光在表面烧录存储、闪存是靠电子擦写存储数据。

Android I/O

1. Android

  • 2
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值