YAFFS2垃圾回收机制完全理解(终级)

回收顺序:
一)回收最老块(500次回收才进行一次回收)
二)回收最老的脏块(有优先回收的前提下)
三)回收最老的优先回收块(有优先回收的前提下)
四)回收最脏(脏指在阀值范围内)且最老的块(正常情况下)
五)回收最老的脏块(这种情况是多次找不到回收块,说明当前的系统很干静,可回收的垃圾很少);


思考点:
1、出现ECC纠正的块,是否有必要超过三次进行坏块标记?
2、出现ECC错误的块,是否有必须回收?
3、垃圾回收时是否可以进行伪坏块检查?制定一个严谨的检查标准?

如何加快yaffs2文件系统垃圾回收机制?
网上常见的说法就是yaffs2文件系统有一个控制阀值,这个阀值控制着yaffs2文件系统的回收,阀值控制相当严格,一般不容易满足条件;但实际上除了这个阀值外yaffs2文件系统还有另外一个控制回收时间的参数,这个参数就是dev->gc_not_done,这个参数在没有找到合理的回收块时会累加,当达到10或者20(两种情况,主要是20),yaffs2文件系统将直接回收最老的脏块,无形中大大放宽了回收标准。如果将这里的10修改为0,那么可以保证在有垃圾可回收的情况下每次都会命中一个回收块,不用再10次一块。当然加不加速,最终都会回收得干干净,仅仅是时间上的问题。
下面是源码修改(加快回收):


在想加快回收时在命令行执行如下操作:echo 0 > /sys/modules/yaffs/parameters/yaffs_gc_not_done
加快一段时间后再恢复到之前的回收速度:echo 10 > /sys/modules/yaffs/parameters/yaffs_gc_not_done

如何确定当前文件系统下有多少可回收的空间?


下面开始YFAAS2文件系统垃圾回收具体分析:

在我们对yaffs2文件系统使用过程中,垃圾回收机制无疑最为重要的一个环节。
垃圾回收处理都是在一个后台运行的进程中执行的,每隔一段时间进行一次回收操作,这个时间间隔由回收的紧迫性决定;而回收的紧迫性又由flash的能使用的空间息息相关,下面具体分析垃圾回收机制。

回收是建立在文件系统被修改后的,文件系统被修改后,dev->is_checkpointed这个参数将会清0,表示文件系统被修改,之前保存的checkpt信息被标记为无效。所以回收的前提是dev->is_checkpointed = 0。当你在命令行界面执行sync后,你会发现垃圾回收机制被关闭了。
    
第一步就是计算回收的紧急程度,以便于计算下一次回收是什么时候;
yaffs_bg_gc_uregency(dev);
这个函数就是计算具体的回收紧急程度。

计算回收紧急程度前先了解两个参数的意义:
1、dev->n_erased_blocks -> 表示未被使用的好块,dev->n_erased_blocks * 64 用C表示;
2、dev->n_free_chunks -> 包括两部分,一部分是未被开发的页,用C表示;另一部分是被删除后无效数据页,用B表示;

    


如果B类型小于64,表示紧急代号为0,不紧急,下次回收间隔为HZ * 2;
如果C类型大于(B+C)的二分之一,表示紧急代号为0,不紧急,下次回收间隔为HZ * 2 ;
如果C类型大于(B+C)的四分之一,小于(B+C)的二分之一,表示紧急代号为1,较紧急,下次回收间隔为HZ / 10 + 1;
其它表示紧急代号为2,非常紧急,下次回收间隔为HZ / 20 + 1。

计算完紧急序列后,下面开始直接的扫描回收块了。

第二步:扫描真实的回收块号。
扫描有两种情况,一种是低力度回收,另一种是高力度回收;力度由flash的使用情况决定,用aggressive标识力度大小。当flash中未被使用的块数量小于保留块时,力度标识为1,力度强;否则力度标识为0,力度普通。

保留块包括为yaffs2文件系统保留的5个块和供checkpoint高速挂载机制使用的块(这个不固定,由文件系统使用情况决定)。

现在不得不认识一个新参数dev->gc_block,它表示当前回收机制正在回收的块,小于1表示当前没有回收块;既然现在是进行回收,那么这个gc_block当且认为它为0。

条件1:如果dev->gc_block < 1 && aggressive = 0表示低回收力度条件下进行周期性刷新查找最老块;这里出现一个新名词,何为最老块?在yaffs2文件系统中,每个块先有一个块号,块号从0开始,另外每个被分配出去的块同时会被分配一个序列号,这个序列号随着一个块被分配后累加(制作烧片时,所有的序列号都相同);被后面被分配出去的序列号就越大,越大说明这个块就越新;反之,序列号越小,说明越早被分配出去,它也就是最老的块了。周期性刷新查找最老块是调用yaffs2_find_refresh_block()函数。
上面提到周期性,多少个周期由参数dev->param.refresh_period决定;yaffs2文件系统默认500个周期刷新一次最老块;代码运行时会将dev->param.refresh_period的值赋给dev->refresh_skip,每一轮(这里指每回收一个块)它会减1,直到减到0,表示现在可以进行最老块扫描,同时将dev->param.refresh_period赋值给dev->refresh_skip,为下一个500次做准备。
扫描最老块就是扫描整个Flash中所有的块中序列号最小的块,下面贴出这部分代码:


每500次后一定会扫描到一个最老块,也就是说500个来回后,dev->gc_block一定不会再小于1。后续代码将会将dev->gc_block对应的块回收掉。

条件2:条件1中未达到扫描到最老块的情况,这时dev->gc_block依然小于1。进一步进行坏块回收扫描。这次扫描与力度有一定的联系。
扫描函数为yaffs_find_gc_block(struct yaffs_dev *dev, int aggressive, int background);
这里又出现一个新名词:优先回收;什么情况下会出现优先回收呢?这个情况有好几种,下面一一列举。
1、写操作失败,主要是写调用底层接口失败;
2、读操作失败,包括读调用底层接口失败、读ECC纠正和读ECC出错。

出现上面的情况都会使当前操作块进入优先回收状态,同时标识整个Flash中有优先回收块。垃圾回收机制运行时读到这个标识有效且力度aggressive为0的情况时,会再次进行回收块扫描;但这次扫描的条件不一样了;首先必须是优先回收的块,其次是标识优先回收的块还必须是最老的块;总的来说,要想这两个条件同时出现,我个人感觉难于上青天,但是它一定还是会被回收,只是早晚的问题;为什么了?因为yaffs2回收总是以最老块为回收标准,只要yaffs2文件系统有写访问,总有一天这时的序列号会成为最老的。

上面如果条件中优先存在(prioritised_exist为真),满足可回收条件的块不存在,但是最老脏块存在,这时这个最老块将会被标识为回收块。这里为什么优先回收最老脏块,而不优先回收标识为优先回收的块了?这是因为标识为优先回收的块基本上都是出现异常的块,这些块回收的风险比较大,故而优先回收最老的块。最老脏块被一一回收了,最后还得回收那个异常的优先块。(这里有一点思考?出现ECC错误的块还有没有必须回收,不怕因错误影响文件系统的正常运行?)

这里还有一点思考,在有优先回收块的时候,回收原则是优先+最老脏块,优先这个条件不难出现,但是最老脏块就不一定了;如果说这个优先块被优先时没有一个脏页(也就是64个全为有效数据),那就不存在脏的说法,最终这个块将无法在优先机制这块被回收?想被回收也要等到N个500后才有可能被回收?

上面是有优先回收标识的情况,正常情况下是不会出现优先回收这回事;现在开始走正常化路线。正常回收路线是基于flash的一段范围进行扫描的;这个范围就由我们上面分析过的力度决定。
分析前先了解两个参数:
1、threshold:这个参数可以认为是一个回收阀值;对于一个块中,如果有效数据chunk最少于这个阀值,那么这个块基本中也就可以回收了(有前提条件的)。
2、iterations:这个参数可以认为是扫描回收的一个范围;在力度低的情况下,我并不去扫描所有的块,因为扫描所有块这个动作是相当耗时的,得不偿失。

n_blocks = dev->internal_end_block - dev->internal_start_block + 1;
力度大时:
threshold = 64; 
iterations = n_blocks;
力度小时:
threshold = 4; 
iterations = n_blocks / 16 + 1;(这个参数最大值为100,也就是力度低时,最多在100个块中回收)。

上面计算了回收阀值与回收范围,现在开始正式回收扫描。
这里先再了解两个参数:
1、dev->gc_dirtiest -> 表示设备上可回收的最脏块(注意这里并不是表示块上所有的数据都无效,仍然 存在有效数据);
2、dev->gc_pages_in_use -> 表示被回收的最脏页中有效数据 的chunk数,其实这个值也就是相当于回收的一个阀值了,扫描时,会用这个参数与阀值对比,看是否需要进行回收。

这两个参数默认情况下应该都是小于1的;
这里还有一个参数需要了解,dev->gc_block_finder,这个参数记录的是上次回收范围的结束块;对于低力度回收时,我们扫描的空间只是整个flash的中的一个小范围,每次回收完成后,下次回收接着上次的结束块继续扫描一段范围。
下面是计算最脏块的条件:
1、块为满块,所有chunk都被申请过;
2、有效数据chunk数量小于64,我这里写成64,这个参数跟flash的物理性质有关;(这个条件有必要?)
3、当前不存在最脏块或者有效数据chunk数量小于dev->gc_pages_in_use;
4、块为最老脏块。
如果条件成立记录当前块为dev->gc_dirtiest,同时记录dev->gc_pages_in_use = pages_used;
条件3中后半部分用于甄选最脏块,也就是有效数据chunk最少。

在当前范围内扫描后,如果最脏块扫描出现来了,且dev->gc_pages_in_use这个参数小于等于阀值threshold,那么表示这个块可以进行回收,否则表示没有找到回收块;
if (dev->gc_dirtiest > 0 && dev->gc_pages_in_use <= threshold)
    selected = dev->gc_dirtiest;
 
到这里我认为还是没有找到回收块,还要继续扫描;
扫描之前了解一个新的参数:dev->gc_not_done,这个参数表示没有找到可以回收的次数,如果gc_not_done超过一定的限额,就表示yaffs2的当前回收很干净了或者资源已经十分紧张了,这时就需要降低回收的标准,如果何降低呢?
这里往回走一下,之前在计算回收阀值时,有提到threshold这个参数,它由dev->gc_not_done决定,dev->gc_not_done这个参数越大,threshold就越大,回收标准也就越低。而dev->gc_not_done这个参数由回收失败次数决定;当dev->gc_not_done这个参数超过10后,情况又变了!
什么情况呢?回收不再受任何限制,直接找到一个最老的脏块,回收它(这次的回收不再参照阀值)。没有最老块的话,那说明系统无垃圾可回收或者资源已经快没有了。
系统回收最理想的情况就是dev->gc_not_done累加,每10次一个周期,每个周期都没有找到回收块(系统太干净了)。
资源紧张是网络上某某人的说法,当且不论他是否正确。
无垃圾可回收是我个人验证的结果,当前的系统已经很干净了。

第三步:回收块已经找到,正式进行回收处理。
首先,如果待回收的块是无效掉的checkpt块或者这个块上的数据全部分无效;则直接调用yaffs_block_became_dirty(dev, block)进行擦除回收。回收前,检查当前块是否使用了summary机制,如果使用了,就清掉summary在chunk_bit中的位图,便于回收。

其次,就是特殊情况了,特殊情况处理起来一般都比较麻烦。
后面的处理简而言之就是将整个块中有效数据拷贝到新的块中,拷贝完成后执行回收擦除处理。
这里的拷贝有点特殊,每次回收最多能拷贝5个chunk,也就是说在阀值较大或者最后一种情况下(回收最老脏块),回收块中有效数据chunk一般都是很大的,远远超过5,这时回收机制只能回收一次拷贝5块,直到全部拷贝完成,才回收擦除这个块。
 

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
YAFFS2(Yet Another Flash File System 2)是一种针对嵌入式系统设计的开源嵌入式文件系统。移植YAFFS2到单片机上涉及以下步骤: 1. 了解单片机的硬件和存储器:单片机的硬件架构和存储器类型对文件系统的移植至关重要。需要了解单片机的处理器类型、存储器大小和类型,例如闪存或EEPROM等。 2. 下载和配置YAFFS2:从官方网站或源代码仓库下载YAFFS2的最新版本。根据单片机的硬件和存储器特性,修改YAFFS2的配置文件,例如设置闪存大小、页大小和块大小等。 3. 移植文件操作接口:单片机的操作系统可能不支持标准的文件操作接口,需要根据单片机的特性实现文件系统的操作接口。这些接口包括读写文件、创建删除文件、目录遍历等。 4. 适配存储器驱动程序:YAFFS2需要针对单片机的存储器类型进行适配。根据单片机的存储器接口和特性,修改YAFFS2的底层驱动程序,确保与单片机的存储器正常交互。 5. 编译和链接YAFFS2:使用适当的交叉编译工具链,将YAFFS2编译为单片机可执行的格式。确保编译选项与单片机的体系结构和操作系统匹配。 6. 测试和调试:将移植后的YAFFS2文件系统部署到单片机上,并进行详细的功能测试和性能测试。通过检查文件系统的功能和性能是否符合预期,进行必要的调试和优化。 总之,移植YAFFS2到单片机需要对单片机硬件和存储器有深入的了解,并进行文件操作接口适配和存储器驱动程序适配。合理配置和编译YAFFS2,最后进行测试和调试,确保其正常运行。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值