标签:
Android存储系统如何优化?
答案是我也不知道…
那为什么会想到要写这篇文章哪?主要是因为有天晚上和以前一个同事讨论到Android手机存储系统的优化问题,我想把我知道的写下来。用过Android手机的人可能都会有这么个感觉,就是手机用久了之后系统会越来越慢。慢,其中很重要的一点就是和Android的存储系统有关。我们现在主流手机的内置存储芯片一般都是EMMC,一些旗舰级的Android手机已经在使用UFS接口的存储芯片,而iphone 6S开始更是用上了NVME接口的存储芯片。同事是做linux kernel相关工作的,他在想,是不是可以在kernel层面优化下Android的存储系统,改善下EMMC的坏块管理、磨损均衡等内容。
接触过一年的SSD固件开发,没有接触过EMMC。但是根据我的理解,EMMC其实就是一个弱化版或者叫精简版的SSD,核心原理和思想是一样的。所以我得出的答案是:同事想做的那些事,kernel什么也做不了。首先来一张大图,我们可以看到,磨损均衡、垃圾回收、坏块管理,这些事情已经全部被SSD、EMMC做掉了,主机端在这些事情上可以说是完全透明的。同事的想法其实还停留在若干年前嵌入式系统使用裸的FLASH芯片,需要自己管理这些繁杂事情的年代。
作为一篇科普贴,这篇文章的阅读对象是对当今的FLASH和SSD不甚了解的朋友们,而并不是那些存储界的老鸟们^_^
首先说下nand flash的种类,当今的nand flash主要分为三种:slc、mlc和tlc。slc每个cell存储一个bit,可以表示0或者1。mlc每个cell存储两个bit,可以表示0-3。tlc每个cell存储三个bit,可以表示0-7。
先说说最简单的slc,它是怎么实现一个bit的存储的。看下图,四个黑点是可以加压的地方。floating gate周围包围着一层绝缘层,在初始状态下(也就是擦除状态下),floating gate里没有电子,表示逻辑1。
对这个cell编程就是把这个cell写成逻辑0,这时需要在control gate加高压,在substrate接地,使得电子被强行打穿绝缘层进入floating gate,并被一直困在里面。
而擦除动作就是逆向操作,在substrate加高压,在control gate接地,使得困在floating gate的电子得以释放,恢复逻辑1。
在读操作的时候,往source和drain两边加压,假如floating gate里有电子,会使得这个电路断路,代表逻辑0。
而假如floating gate里没有电子,则会使得这个电路导通,代表逻辑1。
这样,我们就可以通过往source和drain加压后电路是否导通来判断这个cell存储的是1还是0了。譬如这里加上2V的电压,能通就代表逻辑1,不通就代表逻辑。
mlc把打入电子这个动作做得更为精确细腻了,所以通过把电压的区间划分为更精细的四段来表示11、10、01和00。读操作的时候,会尝试1V、2V和3V三次,根据能否导通来确定cell里面存储的值。tlc亦是如此,只是区间变成了8段。
刚才看的一直是一个cell,这里我们可以看到,横向的同一个word line上的cell组成了一个page,写操作的最小操作单元就是一个page。一共有多少个word line就代表这个block上有多少个page,擦除操作的最小操作单元就是一个block。
看了那么多的nand flash原理了,我们来看看实际的产品。这里要介绍几个概念就是package、target、lun(有的地方叫die,一个意思)。package你可以理解为我们日常能见到的封装好的PCB板上的flash芯片。package里包含一个或多个target,每个target独享物理管脚譬如ce、data[7:0],所以各个target之间是完全并行独立的。然后一个target里又可以包含一个或多个lun,lun也是可以独立运行的单元,和target的区别在于lun之间共享物理管脚。
再看一个lun里有可以有一个或者多个plane,plane也是可以独立运行命令的单元。所以总结一下,一块flash芯片,通常来说,容量越大,target、lun、plane就越多,性能就越好,因为这些物理单元都可以独立并行工作,使得效率最大化。这也就使得我们买的手机,自带的存储空间越大,性能就可能越好,其实就是越贵约好了,哈哈!
这里还要补充几个知识点,有些数据有了很大变化,有些则是文档里没有,靠经验口口相传的结论:
所以我们感觉到android系统用久了变慢,我能想到的和存储相关的几点就是:
由于存储卡越用越满,后期发生的写操作带来巨大的写放大,严重影响性能。说到写放大,先要介绍一个名词叫预留空间(Over provisioning),就是说预留一部分存储空间不保存数据。默认情况下,由于1024和1000的误差,至少会有7%多一点点的OP存在,记住,没有OP,SSD是不能工作的,或者说即使可以勉强工作,效率也很差。为什么这么说那?主要就是因为nand flash以块擦以页写的特性。假设我们用零OP,那倒最后,所有的block上所有的page保存的都是有用的数据。当我们想要更新当中某个page,由于不能直接覆盖写,我们需要把整个block腾出来,然后擦掉它,再把更新好的数据写回去。从上面的图可以看到,现在一个block的大小有2MB(还有更大的)。对有DRAM的SSD而言,也许还能把这个block的数据缓存在DRAM中,但对没有DRAM的低端SSD和emmc这种设备,根本不会有那么大的ram来缓存这些数据,那就陷入了死循环了。所以我们必须预留OP来做垃圾回收。
OP是可以改变的,有的是厂商改,有的则是开放给了用户改(譬如我家里的三星ssd),但是猜想emmc给用户改OP的概率不大。
那如何提升性能哪?接下来就可以介绍写放大(Write amplification)了。写放大的定义就是主机要写一份数据,而由于flash按块擦按页写的特性,N份数据需要挪动到新的物理位置。最后ssd主控为了实现主机的一份写而真是操作了N+1份写,那写放大就是N+1倍。那到底为什么会这样那?譬如我们就拿7%的OP来举例。在长期使用之后,我们可以认为有用的数据和脏数据是很均匀的分部在flash上的。所以在满盘的情况下,一个block假设如上图有256个page,那差不多是238个存着有用的数据,而18(7%*256)个存着已经不要了的垃圾数据。为了写新数据,就需要搬动老的数据。此时,不论盘有多满,固件必须保证有一个block是空的,然后就把刚才那个block的238个有用page搬过来,再在后面写入新的page,当然,之前那个block则又可以擦除了。为了写这18个page,固件实际写了256个page,写放大十几倍,性能就非常的差了,flash的寿命也会下降比较快。
然而对于这些,kernel已经完全被屏蔽了,kernel读写emmc,只会告诉它想要读写的逻辑地址LBA,emmc的FTL(Flash translation layer)负责把这个逻辑地址转换为真正nand上的物理地址。连真正的地址都不知道的kernel,完全就是透明的了。而且现在的nand flash存在着各种坑,emmc这种形式的好处就是把需要关心的细节留给了emmc的固件开发人员。所谓术业有专攻,把这些复杂的东西留给专业的人搞,其实是个不错的选择,要不然广大手机厂商还要自己关心这些细节,负担大了不说,做出来的性能也不见得更好。
最后给出一张很权威很经典的图。linux的存储系统要做优化,从kernel层面我觉得可行的是自上而下优化file system、block layer、mmc driver。但是具体细节我也不懂,感叹一声学无止尽啊!
标签:
原文地址:http://blog.csdn.net/panzhenjie/article/details/51785135