yaffs2源代码分析(最新版)

· 曾经,有位叫斑点的大牛,写了一篇yaffs2源代码分析,全网都在转载,但是我研读这片好文的时候,真心不爽,因为这篇所使用的yaffs2源代码太老了,现在看很多都对不上啊,那就本人自告奋勇的把里面的源码更新一遍,整体重新学习一遍,奉献给大家!
· 在更新的时候,我发现yaffs2这么多年来,核心的代码变化不大,所以在复制时发现找对地方之后,代码就没什么变化,有变化的地方,本文会当场指出。
· 本文使用的是 2019-01-31的版本,下载请见http://www.aleph1.co.uk/gitweb/?p=yaffs2.git;a=summary中的如下位置可下载:
在这里插入图片描述

1.前言

· 略。

2.yaffs 文件系统简介

· 按理说这里应该出现一些诸如“yaffs 是一种适合于 NAND Flash 的文件系统 XXXXX”之类的字眼,不过考虑到网络上关于 yaffs/yaffs2 的介绍已经多如牛毛,所以同上,略。

3.本文内容组织

·本文将模仿《linux 内核源代码情景分析》一书,以情景分析的方式对 yaffs2 文件系统的源代码进行分析。首先将分析几组底层函数,如存储空间的分配和释放等;其次分析文件逻辑地址映射;然后是垃圾收集机制;接下来…Sorry,本人还没想好。😃

4.说明

· 因为 yaffs2 貌似还在持续更新中,所以本文所列代码可能和读者手中的代码不完全一致。
另外,本文读者应熟悉 C 语言,熟悉 NAND Flash 的基本概念(如 block 和 page)。
Ok,步入正题。首先分析存储空间的分配。

5.NAND Flash 存储空间分配和释放

· 我们知道 NAND Flash 的基本擦除单位是 Block,而基本写入单位是 page。yaffs2 在分配存储空间的时候是以 page 为单位的,不过在 yaffs2 中把基本存储单位称为 chunk,和page 是一样的大小,在大多数情况下和 page 是一个意思。在下文中我们使用 chunk 这个词,以保持和 yaffs2 的源代码一致。

5.1 chunk 分配实现

· 我们先看存储空间的分配(在 yaffs_guts.c 中。这个文件也是 yaffs2 文件系统的核心部分):

static int yaffs_alloc_chunk(struct yaffs_dev *dev, int use_reserver,
			     struct yaffs_block_info **block_ptr)
{
   
   
	int ret_val;
	struct yaffs_block_info *bi;

	if (dev->alloc_block < 0) {
   
   
		/* Get next block to allocate off */
		dev->alloc_block = yaffs_find_alloc_block(dev);
		dev->alloc_page = 0;
	}

· 函数有三个参数, dev 是 yaffs_dev 结构的指针, yaffs2 用这个结构来记录一个 NAND器件的属性(如 block 和 page 的大小)和 系统运行过程中的一些统计值(如器件中可用chunk 的总数),还用这个结构维护着一组 NAND 操作函数(如读、写、删除)的指针。
· 整个结构体比较大, 我们会按情景的不同分别分析。use_reserver 表示是否使用保留空间。yaffs2 文件系统并不会将所有的存储空间全部用于存储文件系统数据,而要空出 部分block 用于垃圾收集时使用。一般情况下这个参数都是 0,只有在垃圾收集时需要分配存储空间的情况下将该参数置 1。yaffs_block_info 是描述 block 属性的结构,主要由一些统计变量组成,比如该 block 内还剩多少空闲 page 等。我们同样在具体情景中再分析这个结构中的字段含义。
· 函数首先判断 dev->alloc_block 的值是否小于 0。yaffs_dev 结构内的 alloc_block 字段用于 记录当前从中分配 chunk(page)的那个 block 的序号。当一个 block 内的所有 page 全部分配完毕时,就将这个字段置为-1,下次进入该函数时就会重新挑选空闲的 block。这里我们假定需要重新挑选空闲 block,因此进入
yaffs_find_alloc_block 函数:
[yaffs_alloc_chunk() => yaffs_find_alloc_block()]

static int yaffs_find_alloc_block(struct yaffs_dev *dev)
{
   
   
	u32 i;
	struct yaffs_block_info *bi;

	if (dev->n_erased_blocks < 1) {
   
   
		/* Hoosterman we've got a problem.
		 * Can't get space to gc
		 */
		yaffs_trace(YAFFS_TRACE_ERROR,
		  "yaffs tragedy: no more erased blocks");
		return -1;
	}

· dev->n_erased_blocks 记录着器件内所有可供分配的 block 的数量。如果该值小于 1,那显然是有问题了。不但正常的分配请求无法完成,就连垃圾收集都办不到了。

	for (i = dev->internal_start_block; i <= dev->internal_end_block; i++) {
   
   
		dev->alloc_block_finder++;
		if (dev->alloc_block_finder < (int)dev->internal_start_block
		    || dev->alloc_block_finder > (int)dev->internal_end_block) {
   
   
			dev->alloc_block_finder = dev->internal_start_block;
		}

· internal_start_block 和 internal_end_block 分别是 yaffs2 使用的 block 的起始序号和结束序号。也就是说 yaffs2 文件系统不一定要占据整个 Flash,可以只占用其中的一部分。dev->alloc_block_finder 记录着上次分配的块的序号。如果已经分配到系统尾部,就从头重新开始搜索可用块。

		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 left" ,
			   dev->alloc_block_finder, dev->seq_number,
			   dev->n_erased_blocks);
			return dev->alloc_block_finder;
		}
	}

· yaffs_get_block_info 函数获取指向 block 信息结构的指针,该函数比较简单,就不详细介绍了。yaffs_block_info 结构中的 block_state 成员描述该 block 的状态,比如空,满,已损坏,当前分配中,等等。因为是要分配空闲块,所以块状态必须是YAFFS_BLOCK_STATE_EMPTY,如果不是,就继续测试下一个 block。找到以后将 block 状态修改为 YAFFS_BLOCK_STATE_ALLOCATING,表示当前正从该 block 中分配存储空间。正常情况下,系统中只会有一个 block 处于该状 态。另外还要更新统计量 n_erased_blocks 和seq_number。这个 seq_number 记录着各 block 被分配出去的先后顺序,以后在垃圾收集的时候会以此作为判断该 block 是否适合回收的依据。
· 现在让我们返回到函数 yaffs_alloc_chunk 中。yaffs_check_alloc_available()函数检查 Flash 上是否有足够的可用空间,通过检查后,就从当前供分配的 block 上切下一个chunk:

	if (dev->alloc_block >= 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 (dev->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->alloc_page 记录着上次分配的 chunk 在 block 中的序号,每分配一次加 1。从这里我们可以看出,系统在分配 chunk 的时候是从 block 的开头到结尾按序分配的,直到一个 block 内的所有 chunk 全部分配完毕为止。ret_val 是该 chunk 在整个 device 内的总序号。pages_in_use 记录着该 block 中已分配使用的 page 的数量。系统在设备描述结构 yaffs_dev 中维护着一张位图,该位图的每一位都代表着 Flash上的一个 chunk 的状态。yaffs_set_chunk_bit()将刚分配得到的 chunk 在位图中的对应位置1,表明该块已被使用。更新一些统计量后,就可以返回了。

5.2 chunk 释放实现

看过 chunk 分配以后,我们再来 chunk 的释放。和 chunk 分配不同的是,chunk 的释放在大多数情况下并不释放对应的物理介质,这是因为 NAND 虽然可以按 page 写,但只能按 block 擦除,所以物理介质的释放要留到垃圾收集或一个 block 上的所有 page 全部变成空闲的时候才进行。 根据应用场合的不同,chunk 的释放方式并不唯一,分别由yaffs_chunk_del 函数和 yaffs_soft_del_chunk 函数完 成。我们先看 yaffs_chunk_del:

void yaffs_chunk_del(struct yaffs_dev *dev, int chunk_id, int mark_flash, int lyn)

· chunk_id 就是要删除的 chunk 的序号,mark_flash 参数用于 yaffs 一代的代码中,yaffs2 不
使用该参数。
· 参数 lyn 在调用该函数时置成当前行号(LINE), 用于调试。
· 首先通过 yaffs_get_block_info 获得 chunk 所在 block 的信息描述结构指针,然后就跑到
else 里面去了。if 语句的判断条件中有一 条!dev->param.is_yaffs2,所以对于 yaffs2 而言是不
会执行 if 分支的。在 else 分支里面只是递增一下统计计数就出来了,我们接着往下看。

	if (bi->block_state == YAFFS_BLOCK_STATE_ALLOCATING ||
	    bi->block_state == YAFFS_BLOCK_STATE_FULL ||
	    bi->block_state == YAFFS_BLOCK_STATE_NEEDS_SCAN ||
	    bi->block_state == YAFFS_BLOCK_STATE_COLLECTING) {
   
   
		dev->n_free_chunks++;
		yaffs_clear_chunk_bit(dev, block, page);
		bi->pages_in_use--;

		if (bi->pages_in_use == 0 &&
		    !bi
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值