您当前的位置:首页 > 美文摘抄 > 内容

flash存储什么(flash存储的内容和代码实现)

flash存储什么(flash存储的内容和代码实现)

文章目录

UBI简介

闪存存储的内容

代码实现

将闪存数据读入存储器

组织数据结构

卷EBA子系统初始化

损耗平衡子系统初始化

UBI层操作

例如

擦除和写入均衡

擦除机会

擦除条件

03文本

UBI简介

UBI的全称是无序块图像。上图是UBI在系统中的层次结构,最下面是flash层(包括flash控制器,各种flash驱动代码,spi-mem层等。);MTD层是flash层的抽象。闪存可以分成不同的分区,每个分区对应一个MTD设备。UBI层是基于MTD层的更高层。UBI层抽象出逻辑擦除块,每个逻辑擦除块都有一个与前一个对应的物理擦除块。通过这种映射,我们可以添加一些软件算法来达到擦除平衡的目的,从而延长flash的使用寿命。然后是基于UBI层实现和各种文件系统,比如UBIFS。

闪存存储的内容

首先,介绍一些概念:

PEB:物理擦除块是相应闪存上的擦除块。

Leb:逻辑擦除块软件的概念

体积:体积

上图是flash中(或flash的一个分区中)的数据组织结构:

Ubi层通过擦除块来管理闪存,LEB对应软件概念,PEB对应闪存上一个真实的擦除块,每个LEB对应一个PEB。

查一下,多个leb可以组成一个体积,也就是leb可以根据不同的功能分成不同的体积;Valume-layout是ubi中使用的卷,用于存储MTD设备上划分的每个卷的信息。它包含两个leb,这两个leb存储相同的内容,并且互为备份。

往下看,每个PEB的内容包含三个部分:ech(擦除计数器头)、vidh(卷标识符头)和data。具体意思下面会介绍。

代码实现

UBI层的Linux代码实现大致可以归纳为三个方面:

首先,数据存储在flash中,所以需要将flash中的相关信息读入内存,同时可以检查出flash中的坏块。

读取数据内存后,需要按照内部逻辑关系进行组织(比如使用中的PEB在红黑树上管理,闲置的PEB也在红黑树上管理)

有了内存中这些数据的关系,你就可以对它们进行操作(比如读写,卷的添加,删除,扩展等。擦除和平衡)。

将闪存数据读入存储器

UBI初始化时,代码调用过程如上图所示,最后会调用scan_all()函数,遍历MTD设备。

中的每个PEB,从中读取ech和vidh,它们的定义如下。

Ech定义如上,其中:

Ec:表示PEB被擦除的次数。借助这个场,我们可以找出擦除次数最少的PEB,从而达到擦除平衡的目的。

Vid_hdr_offset:表示该PEB中vidh的偏移位置。

Data_offset:表示实际数据在该PEB中的偏移位置。

Vidh定义如上,其中:

Vol_id:表示PEB属于哪个卷。

Lmun:表示体积中LEB的数量。该字段与MTD设备中的peb数量形成映射关系。通过遍历MTD设备的每一个PEB,我们可以知道每一个PEB的情况,是使用、闲置还是损坏。该信息将被临时记录在结构ubi_attach_info结构中。遍历过程中的细节请参考scan_all()函数。

组织数据结构

遍历PEB后,闪存信息将保存在临时结构struct ubi_attach_info中,然后struct ubi_attach_info中的临时信息将保存在全局结构struct ubi_device *ubi_devices中,代码如下:

有三个步骤,即卷的初始化、损耗均衡子系统的初始化和eba(Eraseblock Association)子系统的初始化。让让我们分开来看一看。

卷EBA子系统初始化

如前所述,volume-layout是UBI内部使用的卷,它包含两个leb(相互备份)。PEB的数据内容如上图所示。数据(灰色)部分是struct ubi_vtbl_record的结构数组,记录了当前ubi设备所有卷的信息。ubi_read_volume_table()函数首先遍历临时结构struct ubi_attach_info,找出volumelayout所在的PEB,然后读出struct ubi_vtbl_record结构数组并保存在内存中。即在struct ubi_device的struct ubi_volume * volumes []字段中,初始化后的数组结构如下图所示,其中structubi _ volume * volumes []是指针数组,数组中的每个元素都是struct ubi_volume结构(详见ubi_read_volume_table()函数)。

在struct ubi_volume结构中,有一个重要的字段struct ubi_eba_table *eba_tbl,记录了当前卷中所有leb和peb之间的映射关系,其中struct ubi_eba_entry *entries是一个数组结构。每个元素对应一个struct ubi_eba_table结构,struct ubi _ eba _ entry *条目数。

组的下标对应LEB的个数,数组元素的内容对应EB的个数,从而将LEB与PEB关联起来(详见ubi_eba_init()函数)。

损耗平衡子系统初始化

在UBI中,PEB分为四种情况:在用、闲置、需要擦除和损坏。每个州的PEB由不同的红黑树管理。在ubi_eba_init()函数中,将分配一个结构ubi_wl_entry指针数组,并存储在sru tubi _ wl _ entry * *查找TBL字段中。数组的下标是PEB的个数,数组的内容记录了PEB的擦写次数。

对于数字信息,每个PEB都有一个这样的结构,对应于下图。

此外,每个PEB根据它们的状态由不同的红黑树管理。使用、空闲、擦洗三种状态的红黑树如上图所示,其中红黑树按照擦除次数的顺序排列,擦除次数最小的在最左侧。如果擦除次数相同,则比较PEB数,较小的在树的左侧,对应的值是struct ubi_wl_entry指针数组中的一个元素。

ubi_eba_init()函数被调用后,损耗均衡子系统被初始化,内存中会形成上图中的数组关系。

UBI层操作

经过前面的初始化,每个数据的结构关系已经保存在内存中,所以UBI层的操作其实就是这些数据在内存中的操作。

从用户空间来看,UBI在初始化后会对应三种类型的字符设备,分别是/dev/ubi_ctrl、/dev/UBIX (x=0,1,2。),/dev/ubix_y (x=0,1,2。y=0,1,2),它们对应的运算函数如下。

Ubi_vol_cdev_operations:针对某个卷(/dev/ubi1_0等)进行操作。).从体积上看,只能看到其中所包含的PEB,所以它的运作也是围绕着PEB展开的。

Ubi_cdev_operations:用于操作Ubi设备(/deb/ubi0等)。).从UBI设备的角度来看,您可以看到不同的卷,因此您可以创建、删除、扩展等等。

Ubi_ctrl_cdev_operations:是针对Ubi层的操作(/dev/ubi_ctrl)。从这个角度可以看到UBI设备,所以可以创建和删除UBI设备。

例如

要求:如果要扩展/dev/ubi1_0的卷,应该怎么做?

用户向内核空间传递两个参数volume_id和size。

在内核空间,我们根据volume_id在structubi _ volume * volumes []的数组中找到卷的句柄。

因为需要扩展容量(需要分配更多的leb),所以需要重新分配struct ubi_eba_table *eba_tbl数组,将旧数组中的数据复制到新数组中。

对于新增的LEB,我们需要从自由树中申请,建立LEB和PEB的映射关系,保存在struct ubi_eba_table *eba_tbl的数组中。此外,我们需要更新PEB的ech和vidh,以显示PEB属于该卷。

以上一系列操作是我自己的想法,不是内核实现代码(具体实现可以是ubi_cdev_ioctl()函数)。这里我想表达的是,UBI初始化完成后,每个卷和每个LEB/PEB的关系已经存在于内存中。所以理论上我们可以完成UBI的操作,只是代码实现不同而已;程序=算法数组结构。这里的数组结构已经存在,算法是UBI层的各种运算。这里的代码其实每个人都可以实现,只是有好有坏。好在kernel已经为我们实现了,可以借鉴。其实别人写的文章只能提供一个大概的思路,真正的细节只能在源代码中获取。

擦除和写入均衡

所有闪存块都有有限的使用寿命。如果频繁擦除闪存的PEB,它很快就会损坏。擦除平衡的目的是将擦除操作平均分配到整个闪存上,从而提高闪存的使用寿命。那么如何将擦除操作均匀分布到整个flash中呢?这个条件还是很难达到的,所以我们退一步,修改PEB最大擦除次数和最小擦除次数之差小于某个值的条件。

例如,flash包含20个peb,其中的数字表示peb被擦除的次数。我们同意擦除时间的最大差异是15。现在flash中peb的最小和最大擦除次数分别是10、39。因为它们超过了阈值,所以我们需要想办法增加擦除次数为10次的peb被擦除的几率,减少擦除次数为39次的peb被擦除的几率。具体实现将在后面介绍。

擦除机会

linux内核在以下两个地方调用擦除均衡:

当损耗均衡子系统初始化完成后,它会检查一次是否需要擦除均衡,这是一个初始状态,也是一个检查的机会。

当要擦除一个PEB时,此时擦除次数会增加,这可能满足擦除平衡的要求。这时候,也是检查的机会。

擦除条件

除了上述调用时间,擦除平衡还有其他一些条件。下图显示了擦除平衡的流程图:

当scrub红黑树上有节点时,必须擦除和均衡。在遍历flash的每个PEB时,如果发现从flash中读取的数据发生翻转,就会添加scrub标志,放在scrub红黑树上进行维护,表示需要擦除PEB;擦除均衡时,首先取出擦洗树最左边节点e1,然后从空闲树中找到合适的节点e2,再读取e1对应的PEB的数据。如果读取的数据有任何问题,擦除将会完成。如果没有问题,e1数据将被复制到e2位置,e1数据将被擦除,以完成擦除均衡操作。

当擦洗树中没有节点时,将从已用树中取出最左边的节点e1,并从空闲树中找到合适的节点e2。然后,将检查e2和e1之间的PEB擦除次数之差是否大于阈值。如果是这样,e1数据将被复制到e2并被擦除以完成该擦除。你为什么这么做?原因是使用的树中的节点已经初始化(先擦除整个节点,然后写入ech和vidh,再写入数据,不擦除),所以不会有擦除操作。空闲树中的节点需要在使用前擦除一次。因此,将擦除次数多的PEB放在使用树中以减少被擦除的机会,将擦除次数少的节点放在空闲树中以增加被擦除的机会,从而达到擦除平衡的目的。

此外,在空闲树上选择一个合适的节点。有哪些适合的和节点?最简单的方法是从空闲树的最右边取一个上层节点(擦除次数最多的节点),然后与从已用树中移除的最左边的节点进行比较,看其差值是否超过阈值。然而,实际情况可能更复杂。下面第29行代码是在内核中选择自由树中节点的方法,将最大擦除次数限制在自由树最左边的节点WL _自由_最大_差异。看到上面评论说某些情况下会连续擦除一个或几个peb,所以做了这样的限制。(我没有I don’我不敢想象那是什么样的

原标题:尹:深入剖析针对Flash的Linux UBI子系统代码

来源:【微信微信官方账号:Linuxer】欢迎添加关注!请注明文章出处。

标签:PEB数据FLASH


声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,谢谢。

上一篇: 联想k30t官方刷机包(我要联想K30-T官方刷机包不要个人发的刷机包,一定要官方刷机包)

下一篇: 谈谈你对云南民族服饰文化的认识和理解。(谈谈你对文化自信的理解和认识)



推荐阅读

网站内容来自网络,如有侵权请联系我们,立即删除! | 软文发布 | 粤ICP备2021106084号