ImageVerifierCode 换一换
格式:DOCX , 页数:23 ,大小:29.97KB ,
资源ID:6153825      下载积分:12 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/6153825.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(yaffs2文件系统分析.docx)为本站会员(b****6)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

yaffs2文件系统分析.docx

1、yaffs2文件系统分析yaffs2文件系统分析作者:dreamice1.前言 略。 2.yaffs文件系统简介 按理说这里应该出现一些诸如“yaffs是一种适合于NAND Flash的文件系统XXXXX”之类的字眼,不过考虑到网络上关于yaffs/yaffs2的介绍已经多如牛毛,所以同上,略。 3.本文内容组织 本文将模仿linux内核源代码情景分析一书,以情景分析的方式对yaffs2文件系统的源代码进行分析。首先将分析几组底层函数,如存储空间的分配和释放等;其次分析文件逻辑地址映射;然后是垃圾收集机制;接下来Sorry,本人还没想好。:-) 4.说明 因为yaffs2貌似还在持续更新中,所

2、以本文所列代码可能和读者手中的代码不完全一致。另外,本文读者应熟悉C语言,熟悉NAND Flash的基本概念(如block和page)。 Ok,步入正题。首先分析存储空间的分配。 5.NAND Flash存储空间分配和释放 我们知道,NAND Flash的基本擦除单位是Block,而基本写入单位是page。yaffs2在分配存储空间的时候是以page为单位的,不过在yaffs2中把基本 存储单位称为chunk,和page是一样的大小,在大多数情况下和page是一个意思。在下文中我们使用chunk这个词,以保持和yaffs2的源代 码一致。 我们先看存储空间的分配(在yaffs_guts.c中。

3、这个文件也是yaffs2文件系统的核心部分):Yaffs2中将该函数更名为yaffs_alloc_chunk。static int yaffs_AllocateChunk(yaffs_Device * dev, int useReserve, yaffs_BlockInfo *blockUsedPtr) int retVal;yaffs_BlockInfo *bi; if (dev-allocationBlock allocationBlock = yaffs_FindBlockForAllocation(dev); dev-allocationPage = 0; 函数有三个参数,dev是ya

4、ffs_Device结构的指针,yaffs2用这个结构来记录一个NAND器件的属性(如block和page的大小)和 系统运行过程中的一些统计值(如器件中可用chunk的总数),还用这个结构维护着一组NAND操作函数(如读、写、删除)的指针。整个结构体比较大,我 们会按情景的不同分别分析。useReserve表示是否使用保留空间。yaffs2文件系统并不会将所有的存储空间全部用于存储文件系统数据,而要空出 部分block用于垃圾收集时使用。一般情况下这个参数都是0,只有在垃圾收集时需要分配存储空间的情况下将该参数置1。yaffs_BlockInfo 是描述block属性的结构,主要由一些统计变

5、量组成,比如该block内还剩多少空闲page等。我们同样在具体情景中再分析这个结构中的字段含义。 函数首先判断dev-allocationBlock的值是否小于0。yaffs_Device结构内的allocationBlock字段用于 记录当前从中分配chunk(page)的那个block的序号。当一个block内的所有page全部分配完毕时,就将这个字段置为-1,下次进入该函 数时就会重新挑选空闲的block。这里我们假定需要重新挑选空闲block,因此进入yaffs_FindBlockForAllocation函数: yaffs_AllocateChunk() = yaffs_FindB

6、lockForAllocation()static int yaffs_FindBlockForAllocation(yaffs_Device * dev) int i; yaffs_BlockInfo *bi; if (dev-nErasedBlocks nErasedBlocks记录着器件内所有可供分配的block的数量。如果该值小于1,那显然是有问题了。不但正常的分配请求无法完成,就连垃圾收集都办不到了。 for (i = dev-internalStartBlock; i internalEndBlock; i+) dev-allocationBlockFinder+; if (dev

7、-allocationBlockFinder internalStartBlock | dev-allocationBlockFinder dev-internalEndBlock) dev-allocationBlockFinder = dev-internalStartBlock; internalStartBlock和internalEndBlock分别是yaffs2使用的block的起始序号和结束序号。也就是说yaffs2文件系统不一定要占据整个Flash,可以只占用其中的一部分。 dev-allocationBlockFinder记录着上次分配的块的序号。如果已经分配到系统尾部,就从

8、头重新开始搜索可用块。 bi = yaffs_get_block_info(dev, dev-alloc_block_finder); if (bi-block_state = YAFFS_BLOCK_STATE_EMPTY) bi-block_state = YAFFS_BLOCK_STATE_ALLOCATING; dev-seq_number+; bi-seq_number = dev-seq_number; dev-n_erased_blocks-; yaffs_trace(YAFFS_TRACE_ALLOCATE, Allocated block %d, seq %d, %d lef

9、t , dev-alloc_block_finder, dev-seq_number, dev-n_erased_blocks); return dev-alloc_block_finder; yaffs_GetBlockInfo 函数获取指向block信息结构的指针,该函数比较简单,就不详细介绍了。yaffs_BlockInfo结构中的blockState成员描述该 block的状态,比如空,满,已损坏,当前分配中,等等。因为是要分配空闲块,所以块状态必须是YAFFS_BLOCK_STATE_EMPTY,如果不是,就继续测试下一个block。找到以后将block状态修改为 YAFFS_BLO

10、CK_STATE_ALLOCATING,表示当前正从该block中分配存储空间。正常情况下,系统中只会有一个block处于该状 态。另外还要更新统计量ErasedBlocks和sequenceNumber。这个sequenceNumber记录着各block被分配出去的先后 顺序,以后在垃圾收集的时候会以此作为判断该block是否适合回收的依据。 现在让我们返回到函数 yaffs_AllocateChunk中。yaffs_CheckSpaceForAllocation()函数检查Flash上是否有足够的可用空间,通过检查后,就从当前供分配的block上切下一个 if (dev-alloc_blo

11、ck = 0) bi = yaffs_get_block_info(dev, dev-alloc_block); ret_val = (dev-alloc_block * dev-param.chunks_per_block) + dev-alloc_page; bi-pages_in_use+; yaffs_set_chunk_bit(dev, dev-alloc_block, dev-alloc_page); dev-alloc_page+; dev-n_free_chunks-; /* If the block is full set the state to full */ if (d

12、ev-alloc_page = dev-param.chunks_per_block) bi-block_state = YAFFS_BLOCK_STATE_FULL; dev-alloc_block = -1; if (block_ptr) *block_ptr = bi; return ret_val;dev-allocationPage记录着上次分配的chunk在block中的序号,每分配一次加1。从这里我们可以看出,系统在分配 chunk的时候是从block的开头到结尾按序分配的,直到一个block内的所有chunk全部分配完毕为止。retVal是该chunk在整个 device内的总

13、序号。PagesInUse记录着该block中已分配使用的page的数量。 系统在设备描述结构yaffs_Device 中维护着一张位图,该位图的每一位都代表着Flash上的一个chunk的状态。yaffs_SetChunkBit()将刚分配得到的chunk在位图中的对应位置1,表明该块已被使用。更新一些统计量后,就可以返回了。 看过chunk分配以后,我们再来chunk的释放。和chunk分配不同的是,chunk的释放在大多数情况下并不释放对应的物理介质,这是因为 NAND虽然可以按page写,但只能按block擦除,所以物理介质的释放要留到垃圾收集或一个block上的所有page全部变成空

14、闲的时候才进行。 根据应用场合的不同,chunk的释放方式并不唯一,分别由yaffs_DeleteChunk函数和yaffs_SoftDeleteChunk函数完 成。我们先看yaffs_DeleteChunk:(该函数在后续版本中被更名为yaffs_chunk_del()void yaffs_DeleteChunk(yaffs_Device * dev, int chunkId, int markNAND, int lyn) chunkId就是要删除的chunk的序号,markNand参数用于yaffs一代的代码中,yaffs2不使用该参数。 参数lyn在调用该函数时置成当前行号(_LINE

15、_),用于调试。 首先通过yaffs_GetBlockInfo获得chunk所在block的信息描述结构指针,然后就跑到else里面去了。if语句的判断条件中有一 条!dev-isYaffs2,所以对于yaffs2而言是不会执行if分支的。在else分支里面只是递增一下统计计数就出来了,我们接着往下 看。if (bi-blockState = YAFFS_BLOCK_STATE_ALLOCATING |bi-blockState = YAFFS_BLOCK_STATE_FULL |bi-blockState = YAFFS_BLOCK_STATE_NEEDS_SCANNING |bi-bloc

16、kState = YAFFS_BLOCK_STATE_COLLECTING) dev-nFreeChunks+;yaffs_ClearChunkBit(dev, block, page); bi-pagesInUse-; if (bi-pagesInUse = 0 &!bi-hasShrinkHeader &bi-blockState != YAFFS_BLOCK_STATE_ALLOCATING &bi-blockState != YAFFS_BLOCK_STATE_NEEDS_SCANNING) yaffs_BlockBecameDirty(dev, block); 首先要判断一下该blo

17、ck上是否确实存在着可释放的chunk。block不能为空,不能是坏块。YAFFS_BLOCK_STATE_NEEDS_SCANNING表明正对该块进行垃圾回收,我们后面会分析;YAFFS_BLOCK_STATE_NEEDS_SCANNING在我手上的源代码中似乎没有用到。 通过判断以后,所做的工作和chunk分配函数类似,只是一个递增统计值,一个递减。递减统计值以后还要判断该block上的page是否已全部释放,如 果已全部释放,并且不是当前分配块,就通过yaffs_BlockBecameDirty函数删除该block,只要能通过删除操作(不是坏块),该 block就又可以用于分配了。 相比

18、较来说,yaffs_SoftDeleteChunk所做的工作就简单多了。关键的代码只有两行: static void yaffs_SoftDeleteChunk(yaffs_Device * dev, int chunk) theBlock-softDeletions+;dev-nFreeChunks+; 这里递增的是yaffs_blockInfo结构中的另一个统计量 softDeletions,而没有修改pagesInUse成员,也没有修改chunk状态位图。那么,这两个函数的应用场合有什么区别呢?一般来说,yaffs_DeleteChunk用于文件内容的更新。比如我们要修改文件中的部分内容

19、,这时候yaffs2会分配新的chunk,将更改后的内容写入新chunk中,原chunk的内容自然就没有用了,所以要将pageInUse减1,并修改位图; yaffs_SoftDeleteChunk用于文件的删除。yaffs2在删除文件的时候只是删除该文件在内存中的一些描述结构,而被删除的文件所占用 的chunk不会立即释放,也就是不会删除文件内容,在后续的文件系统操作中一般也不会把这些chunk分配出去,直到系统进行垃圾收集的时候才有选择地 释放这些chunk。熟悉DOS的朋友可能还记得,DOS在删除的文件的时候也不会立即删除文件内容,只是将文件名的第一个字符修改为0xA5,事后还可以恢复文

20、件内容。yaffs2在这点上是类似的。 1.文件地址映射 上面说到,yaffs文件系统在更新文件数据的时候,会分配一块新的chunk,也就是说,同样的文件偏移地址,在该地址上的数据更新前和更新后,其对应 的flash上的存储地址是不一样的。那么,如何根据文件内偏移地址确定flash存储地址呢?最容易想到的办法,就是在内存中维护一张映射表。由于 flash基本存储单位是chunk,因此,只要将以chunk描述的文件偏移量作为表索引,将flash chunk序号作为表内容,就可以解决该问题了。但是这个方法有几个问题,首先就是在做seek操作的时候,要从表项0开始按序搜索,对于大文件会消耗很 多时间

21、;其次是在建立映射表的时候,无法预计文件大小的变化,于是就可能在后来的操作中频繁释放分配内存以改变表长,造成内存碎片。yaffs的解决方法 是将这张大的映射表拆分成若干个等长的小表,并将这些小表组织成树的结构,方便管理。我们先看小表的定义:struct yaffs_tnode struct yaffs_tnode *internalYAFFS_NTNODES_INTERNAL; YAFFS_NTNODES_INTERNAL定义为(YAFFS_NTNODES_LEVEL0 / 2),而YAFFS_NTNODES_LEVEL0定义为16,所以这实际上是一个长度为8的指针数组。不管是叶子节点还是非叶

22、节点,都是这个结构。当节点为非叶 节点时,数组中的每个元素都指向下一层子节点;当节点为叶子节点时,该数组拆分为16个16位长的短整数(也有例外,后面会说到),该短整数就是文件内容 在flash上的存储位置(即chunk序号)。至于如何通过文件内偏移找到对应的flash存储位置,源代码所附文档(Development/yaffs/Documentation/yaffs-notes2.html)已经有说明,俺就不在此处饶舌了。下面看具体 函数。为了行文方便,后文中将yaffs_Tnode这个指针数组称为“一组”Tnode,而将数组中的每个元素称为“一个”Tnode。树中的每个节点,都是“一组”Tn

23、ode。 先看映射树的节点的分配。 struct yaffs_tnode *yaffs_get_tnode(struct yaffs_dev *dev) struct yaffs_tnode *tn = yaffs_alloc_raw_tnode(dev); if (tn) memset(tn, 0, dev-tnode_size); dev-n_tnodes+; dev-checkpoint_blocks_required = 0; /* force recalculation */ return tn;调用yaffs_GetTnodeRaw分配节点,然后将得到的节点初始化为零。 stati

24、c yaffs_Tnode *yaffs_GetTnodeRaw(yaffs_Device * dev) yaffs_Tnode *tn = NULL; /* If there are none left make more */ if (!dev-freeTnodes) yaffs_CreateTnodes(dev, YAFFS_ALLOCATION_NTNODES); 当前所有空闲节点组成一个链表,dev-freeTnodes是这个链表的表头。我们假定已经没有空闲节点可用,需通过yaffs_CreateTnodes创建一批新的节点。 static int yaffs_CreateTnode

25、s(yaffs_Device * dev, int nTnodes) .tnodeSize = (dev-tnodeWidth * YAFFS_NTNODES_LEVEL0)/8; newTnodes = YMALLOC(nTnodes * tnodeSize); mem = (_u8 *)newTnodes; (其实在最新版本的yaffs中已经加入了slab缓冲区,这样提高了效率)上面说过,叶节点中一个Tnode的位宽默认为16位,也就是可以表示65536个chunk。对于时下的大容量flash,chunk的大小为2K,因 此在默认情况下yaffs2所能寻址的最大flash空间就是128M。

26、为了能将yaffs2用于大容量flash上,代码作者试图通过两种手段解决这个 问题。第一种手段就是这里的dev-tnodeWidth,通过增加单个Tnode的位宽,就可以增加其所能表示的最大chunk Id;另一种手段是我们后面将看到的chunk group,通过将若干个chunk合成一组用同一个id来表示,也可以增加系统所能寻址的chunk范围。俺为了简单,分析的时候不考虑这两种情况,因 此tnodeWidth取默认值16,也不考虑将多个chunk合成一组的情况,只在遇到跟这两种情况有关的代码时作简单说明。 在32位的系统中,指针的宽度为32位,而chunk id的宽度为16位,因此相同大小

27、的Tnode组,可以用来表示N个非叶Tnode(作为指针使用),也可以用来表示N * 2个叶子Tnode(作为chunk id使用)。代码中分别用YAFFS_NTNODES_INTERNAL和YAFFS_NTNODES_LEVEL0来表示。前者取值为8,后者取值为16。从这里我们也可以看出若将yaffs2用于64位系统需要作哪些修改。 针对上一段叙述的问题,俺以为在内存不紧张的情况下,不如将叶节点Tnode和非叶节点Tnode都设为一个指针的长度。 分配得到所需的内存后,就将这些空闲空间组成Tnode链表: for(i = 0; i internal0 = next; 每组Tnode的第一个元

28、素作为指针指向下一组Tnode。完成链表构造后,还要递增统计量,并将新得到的Tnodes挂入一个全局管理链表yaffs_TnodeList: dev-nFreeTnodes += nTnodes; dev-nTnodesCreated += nTnodes; tnl = YMALLOC(sizeof(yaffs_TnodeList); if (!tnl) T(YAFFS_TRACE_ERROR, (TSTR (yaffs: Could not add tnodes to management list TENDSTR); else tnl-tnodes = newTnodes; tnl-nex

29、t = dev-allocatedTnodeList; dev-allocatedTnodeList = tnl; 回到yaffs_GetTnodeRaw,创建了若干组新的Tnode以后,从中切下所需的Tnode,并修改空闲链表表头指针: if (dev-freeTnodes) tn = dev-freeTnodes; dev-freeTnodes = dev-freeTnodes-internal0; dev-nFreeTnodes-; 至此,分配工作就完成了。相比较来说,释放Tnodes的工作就简单多了,简单的链表和统计值操作: static void yaffs_FreeTnode(yaffs_Device * dev, yaffs_Tnode * tn) if (tn) tn-internal0 = dev-freeTnodes; dev-freeTnodes = tn; dev-nFreeTnodes+; 看过Tnode的分配和释放,我们再来看看这些Tnode是如何使用的。在后文中,我们把以chunk为单位的文件

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1