fstrim提升磁盘性能,缓解Android卡顿
Android手机刚购买时非常流畅,但是使用不到一年之后都会“卡顿”。那么是什么原因造成的呢?
长期使用Android手机将产生大量的磁盘碎片,而磁盘碎片将会降低磁盘的读写性能,从而影响系统流畅度。
1. Android磁盘的读写机制
Android手机大多数采用NAND flash架构的闪存卡来存储内容,NAND Flash的内部存储单位从小到大依次为:Page、Block、Device,下面是一个NAND Flash组成结构的示意图。
虽然NAND Flash的优点很多,但是为了延长驱动器的寿命,它的读写操作是以Page为单位进行的,但擦除操作确实按照Block为单位进行的。
由于有大量的读写操作,于是NAND Flash制定了如下读写规则:
1.删除数据时,芯片将标记这些Page为闲置状态,但并不会立马执行擦除操作
2.写入数据时,如果目前磁盘剩余充足,则由芯片指定Block后直接按Page为单位进行写入操作。
3.写入数据时,如果目前磁盘剩余空间不足,为了获得足够的空间,磁盘先将某块Block的内容读直缓存,然后再在该Block上进行擦除操作,最后将新内容与原先内容一起写入至该Block。
其实想存储的就是1个Page的图片内容,但是实际上确造成了整个Block的内容都被重新写入,同时原本简单一步搞定的事情被还被分成了前后四步执行(闪存读取、缓存改、闪存擦除、闪存写入)造成延迟大大增加,速度变慢。这就是传说中的“写入放大”(Write Amplification)问题。而“写入放大”也说明了磁盘在长期使用的过程中,其读写速度(尤其是写入速度)会存在降低的现象。
2. 解决“写入放大”问题的技术——TRIM
Google的解决方案TRIM技术。
TRIM是一条ATA指令,由操作系统发送给闪存主控制器,告诉它哪些数据占的地址是“无效”的。在TRIM的帮助下,闪存主控制器就可以提前知道哪些Page是“无效”的,便可以在适当的时机做出优化,从而改善性能。这里要强调下,TRIM只是条指令,让操作系统告诉闪存主控制器这个Page已经“无效”就算完了,并没有任何其它多余的操作。TRIM的触发需要操作系统、驱动程序以及闪存主控三者都支持才能真正意义上实现。例如:
操作系统不支持的情况:Android 4.3以下均不支持
闪存主控不支持的情况:Samsung Galaxy Nexus(I9250)所选用的闪存不支持
基于TRIM技术,目前常见有两种方案可以解决“写入放大”的问题:
discard选项。该方案将在挂载 ext4 分区时加上 discard 选项,此后操作系统在执行每一个磁盘操作时同时都会执行 TRIM 指令。该方案的优点是总体耗时短,但会影响到删除文件时的性能。
fstrim命令。该方案将选择合适的时机对整个分区执行TRIM操作。相对于方案一,该方案总体耗时较长,但不会影响正常操作时的磁盘性能。
从用户的角度出发,还是FSTRIM的方法更靠谱一些。
3. TRIM在Android上实现
在PackageManagerServic中有performFstrimIfNeeded方法,最后执行的是StorageManagerService.fstrim
1 | @Override |
到这来,调用的是mVold,而mVold初始化如下,那么mVold的服务端在哪呢?
1 | binder = ServiceManager.getService("vold"); |
4. Vold守护进程启动
Vold负责挂载SD卡,vold的全称是volume daemon,实际上负责完成系统的CDROM
USB大容量存储,MMC卡等拓展存储的挂载任务自动完成的守护进程。它提供的主要特点是支持这些存储设备外设的热插拔。
Vold相关的代码在/system/vold目录中
对应的启动在vold.rc中,其内容如下:
1 | service vold /system/bin/vold \ |
上面定义了一个vold的service,去执行vold程序,对应的入口在vold目录下的main.cpp中。
main.cpp中主要是创建了VolumeManager、NetlinkManager的实例,并且启动了VoldNativeService服务
1 | int main(int argc, char** argv) { |
上面mVold.fstrim方法是通过binder远程调用了VoldNativeService::fstrim方法
1 | binder::Status VoldNativeService::fstrim(int32_t fstrimFlags, |
android::vold::Trim方法在IdleMaint.cpp中
1 | void Trim(const android::sp<android::os::IVoldTaskListener>& listener) { |