Yaffs 全称 Yet Another Flash File System,是一种嵌入式设备常用文件系统。它对基于Flash的存储设备(NAND 或NOR Flash)特别是NAND Flash有很好的支持,其具有速度快、鲁棒性强等特点。此外,其对各种操作系统的支持也很好。
概述 先看Yaffs在整个系统中的位置(注:本文无特别说明均以搭载Linux OS为例):
YAFFS位于虚拟文件系统之下,为VFS提供调用接口
YAFFS使用Linux MTD 子系统提供的服务对Flash进行操作。
为什么直接使用Nand或者Nor Flash 驱动直接操作?这就涉及到Linux内核的设计思想:尽可能的对架构进行抽象,避免上下层直接调用,产生N*N的组合,节省了内核驱动设计者的开发负担,同时降低系统的代码复杂度。 
 
+—————————+
 
  +—————————+
  +—————————+
Yaffs2主要有以下功能模块
Flash的块/Chunk分配管理 
检查点机制 
垃圾回收机制 
缓存机制 
Proc调试功能 
 
YAFFS文件系统格式 如下图,按照由大到小,Flash一般如下组成:
Block(块):Block一般为Flash擦除的基本单位。由多个页及对应OOB区域组成, 跟据Flash控制器和Flash颗粒的不同, 组成Block的page数量也不相同 
Page(页):Flash的页,用来存放真正数据的基本单位。在Yaffs文件系统管理里称作数据Chunk。 
OOB(带外数据):与页对应,紧跟在页的后边,一般存放ECC校验信息,Flash坏块标记等信息。 
 
+——————–+
一个典型的普通文件在 YAFFS 文件系统格式Flash的存储形式如下图所示:
YAFFS将文件头部信息(包括文件类型,文件名,父目录的obj_id等) 
文件的唯一标示id(也叫obj_id)位于OOB区域,紧跟chunk_id标示这个是文件的第几块数据,Chunk_id 0表示该page为文件头信息,不含文件内容 
真正的文件内容根据文件大小散落在的size/page_size+1个页内。若文件大小正好不是页大小的整数倍,那么最后一段文件内容后会填充全0xFF放在一个页内,Page对应的OOB区域 
因为文件可能会被YAFFS管理(例如,上层应用对文件改写)的关系,同一个文件的内容不一定会摆放在相邻的Flash 页内(刚刚使用mkyaffs创建的文件系统除外)。同时,对同一个文件,可能会存在多个相同obj_id肯chunk_id的文件页,此时有另外的标示seq number来决定选择哪个page 
 
+—————————-+
源码结构 真正开始源码分析前,我们先看一下YAFFS的源码结构。Yaffs的核心代码如下:
yaffs_allocator.c
yaffs_obj和Tnode的分配器
yaffs_bitmap.c
管理块和Chunk的位图的代码  
yaffs_checkpointrw.c
读写Checkpoint数据的代码  
yaffs_ecc.c
Yaffs ECC相关代码(使用Hamming ECC )  
yaffs_guts.c
Yaffs 主代码.
yaffs_nameval.c
处理Yaffs扩展标签的diamante (xattr).
yaffs_nand.c
Flash 接口抽象.
yaffs_packedtags1.c
打包Yaffs标签代码
yaffs_summary.c
处理Flash块Summary的代码.
yaffs_tagscompat.c
兼容Yaffs1模式的标签处理代码  
yaffs_tagsmarshall.c
标签组织代码
yaffs_verify.c
用于检查有效性的代码(例如,头部格式检查等)
yaffs_yaffs1.c
Yaffs1 模式特有代码.
yaffs_yaffs2.c
Yaffs2 模式特有代码.
yaffs_attribs.c  
文件属性处理相关代码
yaffs_error.c
存放Yaffs 错误代码
yaffsfs.c
Yaffs文件系统接口wrapper(支援多文件系统时用)
yaffs_hweight.c
用来检查Flash坏块标记的一些功能代码
yaffs_qsort.c
Yaffs使用的一些排序函数
初始化 YAFFS的初始化很简单,源代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 module_init(init_yaffs_fs) static  int  __init init_yaffs_fs (void ) 	int  error = 0 ; 	struct  file_system_to_install  *fsinst ; 	mutex_init(&yaffs_context_lock);         	error = yaffs_procfs_init();                        ----1  	if  (error) 		return  error; 	fsinst = fs_to_install; 	while  (fsinst->fst && !error) { 		error = register_filesystem(fsinst->fst);   ----2  		if  (!error) 			fsinst->installed = 1 ; 		fsinst++; 	} 	                        ----3  	if  (error) { 		fsinst = fs_to_install; 		while  (fsinst->fst) { 			if  (fsinst->installed) { 				unregister_filesystem(fsinst->fst); 				fsinst->installed = 0 ; 			} 			fsinst++; 	}} 	return  error; } static  struct  file_system_to_install  fs_to_install [] = {	{&yaffs_fs_type, 0 }, 	{&yaffs2_fs_type, 0 }, 	{NULL , 0 } }; static  struct  file_system_type  yaffs2_fs_type  = {	.owner = THIS_MODULE, 	.name = "yaffs2" , 	.mount = yaffs2_mount, 	.kill_sb = kill_block_super, 	.fs_flags = FS_REQUIRES_DEV, }; 
第1段初始化系统的procfs 
第2段向Kernel注册yaffs文件系统,这里不光注册yaffs2也会注册yaffs1,所以可以看到是一个while循环 
第3段:如果注册文件系统发生问题,解注册文件系统,此处不做过多分析 
 
数据结构 被成功注册到内核后,YAFFS利用内存中如下重要的数据结构来对文件系统进行管理:
_yaffs_dev: _YAFFS Partition或者挂载点的相关信息 
_yaffs_block_info :_Nand Flash 块的信息,一个yaffs partition会有多个block info 
_yaffs_obj: _记录文件系统中的文件或者目录相关信息 
_yaffs_tnode: _记录文件系统的目录结构,以及文件chunk位置相关信息 
 
此外,YAFFS会使用少量的内存缓存(Cache)来提升访问性能。
yaffs_dev定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 struct  yaffs_dev  {	struct  yaffs_param  param ; 	struct  yaffs_driver  drv ; 	struct  yaffs_tags_handler  tagger ;          	void  *os_context; 	void  *driver_context; 	struct  list_head  dev_list ; 	int  ll_init; 	u32 data_bytes_per_chunk; 	u16 chunk_grp_bits; 	u16 chunk_grp_size;          	struct  yaffs_tnode  *tn_swap_buffer ; 	u32 tnode_width; 	u32 tnode_mask; 	u32 tnode_size;          	u32 chunk_shift;	 	u32 chunk_div;		 	u32 chunk_mask;		          	int  is_mounted; 	int  read_only; 	int  is_checkpointed; 	int  swap_endian;	          	u32 internal_start_block; 	u32 internal_end_block; 	int  block_offset; 	int  chunk_offset;          	int  checkpt_page_seq;         .............. 	 	struct  yaffs_block_info  *block_info ; 	u8 *chunk_bits;		 	u8 block_info_alt:1 ;	 	u8 chunk_bits_alt:1 ;	 	int  chunk_bit_stride;	 	int  n_erased_blocks; 	int  alloc_block;	 	u32 alloc_page; 	int  alloc_block_finder;	 	 	void  *allocator; 	int  n_obj; 	int  n_tnodes; 	int  n_hardlinks; 	struct  yaffs_obj_bucket  obj_bucket [YAFFS_NOBJECT_BUCKETS ]; 	u32 bucket_finder; 	int  n_free_chunks;           	u32 *gc_cleanup_list;        ................ 	 	struct  yaffs_obj  *root_dir ; 	struct  yaffs_obj  *lost_n_found ; 	int  buffered_block;	 	int  doing_buffered_block_rewrite;          	struct  yaffs_cache  *cache ; 	int  cache_last_use; 	 	struct  yaffs_obj  *unlinked_dir ; 	struct  yaffs_obj  *del_dir ; 	struct  yaffs_obj  *unlinked_deletion ; 	int  n_deleted_files;	 	int  n_unlinked_files;	 	int  n_bg_deletions;	 	 	struct  yaffs_buffer  temp_buffer [YAFFS_N_TEMP_BUFFERS ]; 	int  max_temp; 	int  temp_in_use; 	int  unmanaged_buffer_allocs; 	int  unmanaged_buffer_deallocs; 	unsigned  seq_number;	 	unsigned  oldest_dirty_seq; 	unsigned  oldest_dirty_block; 	int  refresh_skip;	 	struct  list_head  dirty_dirs ; 	int  chunks_per_summary; 	struct  yaffs_summary_tags  *sum_tags ; 	 	u32 n_page_writes;         ....... }; 
yaffs_param 主要包含文件系统相关参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 struct  yaffs_param  {	const  YCHAR *name;       	int  inband_tags;	 	u32 total_bytes_per_chunk; 	u32 chunks_per_block;  	u32 spare_bytes_per_chunk; 	u32 start_block;          u32 end_block;	 	u32 n_reserved_blocks;  	u32 n_caches; 	int  cache_bypass_aligned;  	int  use_nand_ecc;	 	int  tags_9bytes;	 	int  no_tags_ecc;	 	int  is_yaffs2;		 	int  empty_lost_n_found;	 	int  refresh_period;	 	u8 skip_checkpt_rd;      	u8 skip_checkpt_wr; 	int  enable_xattr;	 	int  max_objects;	 	int  hide_lost_n_found;   	int  stored_endian;       	void  (*remove_obj_fn) (struct yaffs_obj *obj);  	void  (*sb_dirty_fn) (struct yaffs_dev *dev); 	unsigned  (*gc_control_fn) (struct yaffs_dev *dev);          	int  use_header_file_size;	 	int  disable_lazy_load;	 	int  wide_tnodes_disabled;	 	int  disable_soft_del;	 	int  defered_dir_update;	 	int  auto_unicode; 	int  always_check_erased; 	int  disable_summary; 	int  disable_bad_block_marking; }; 
以下为yaffs标签相关处理函数,在yaffs_tags_marshall_install函数初始化。源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 struct  yaffs_tags_handler  {	int  (*write_chunk_tags_fn) (struct yaffs_dev *dev,int  nand_chunk, const  u8 *data,const  struct yaffs_ext_tags *tags); 	int  (*read_chunk_tags_fn) (struct yaffs_dev *dev, int  nand_chunk, u8 *data,struct yaffs_ext_tags *tags); 	int  (*query_block_fn) (struct yaffs_dev *dev, int  block_no,enum  yaffs_block_state *state, u32 *seq_number); 	int  (*mark_bad_fn) (struct yaffs_dev *dev, int  block_no); }; void  yaffs_tags_marshall_install (struct yaffs_dev *dev) 	if  (!dev->param.is_yaffs2) 		return ; 	if  (!dev->tagger.write_chunk_tags_fn) 		dev->tagger.write_chunk_tags_fn = yaffs_tags_marshall_write; 	if  (!dev->tagger.read_chunk_tags_fn) 		dev->tagger.read_chunk_tags_fn = yaffs_tags_marshall_read; 	if  (!dev->tagger.query_block_fn) 		dev->tagger.query_block_fn = yaffs_tags_marshall_query_block; 	if  (!dev->tagger.mark_bad_fn) 		dev->tagger.mark_bad_fn = yaffs_tags_marshall_mark_bad; } 
yaffs_driver定义了Yaffs驱动相关,读写擦除,坏块检查等操作MTD设备的接口函数,其初始化位于yaffs_mtd_drv_install函数。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 struct  yaffs_driver  {	int  (*drv_write_chunk_fn) (struct yaffs_dev *dev, int  nand_chunk, const  u8 *data, int  data_len, const  u8 *oob, int  oob_len); 	int  (*drv_read_chunk_fn) (struct yaffs_dev *dev, int  nand_chunk, u8 *data, int  data_len,u8 *oob, int  oob_len,enum  yaffs_ecc_result *ecc_result); 	int  (*drv_erase_fn) (struct yaffs_dev *dev, int  block_no); 	int  (*drv_mark_bad_fn) (struct yaffs_dev *dev, int  block_no); 	int  (*drv_check_bad_fn) (struct yaffs_dev *dev, int  block_no); 	int  (*drv_initialise_fn) (struct yaffs_dev *dev); 	int  (*drv_deinitialise_fn) (struct yaffs_dev *dev); }; void  yaffs_mtd_drv_install (struct yaffs_dev *dev) 	struct  yaffs_driver  *drv  = &dev ->drv ; 	drv->drv_write_chunk_fn = yaffs_mtd_write; 	drv->drv_read_chunk_fn = yaffs_mtd_read; 	drv->drv_erase_fn = yaffs_mtd_erase; 	drv->drv_mark_bad_fn = yaffs_mtd_mark_bad; 	drv->drv_check_bad_fn = yaffs_mtd_check_bad; 	drv->drv_initialise_fn = yaffs_mtd_initialise; 	drv->drv_deinitialise_fn = yaffs_mtd_deinitialise; } 
以下为Yaffs选项,为yaffs系统挂载(Mount)时配置,最后被传给yaffs_dev对应栏位。
1 2 3 4 5 6 7 8 9 10 11 12 struct  yaffs_options  {	int  inband_tags;          	int  skip_checkpoint_read; 	int  skip_checkpoint_write; 	int  no_cache; 	int  tags_ecc_on; 	int  tags_ecc_overridden; 	int  lazy_loading_enabled; 	int  lazy_loading_overridden; 	int  empty_lost_and_found; 	int  empty_lost_and_found_overridden; }; 
yaffs对应Linux对应上下文:
1 2 3 4 5 6 7 8 9 10 11 12 13 struct  yaffs_linux_context  {	struct  list_head  context_list ; 	struct  yaffs_dev  *dev ; 	struct  super_block  *super ; 	struct  task_struct  *bg_thread ; 	int  bg_running;  	struct  mutex  gross_lock ; 	u8 *spare_buffer;	 	struct  list_head  search_contexts ; 	struct  task_struct  *readdir_process ; 	unsigned  mount_id;  	int  dirty; }; 
文件系统挂载 挂载后,整个文件系统才会为操作系统所用。YAFFS文件系统的挂载过程如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 static  struct dentry *yaffs2_mount (struct file_system_type *fs_type, int  flags,const  char  *dev_name, void  *data)         return  mount_bdev(fs_type, flags, dev_name, data, yaffs2_internal_read_super_mtd);    } static  int  yaffs2_internal_read_super_mtd (struct super_block *sb, void  *data, int  silent) 	return  yaffs_internal_read_super(2 , sb, data, silent) ? 0  : -EINVAL; } static  struct super_block *yaffs_internal_read_super (int  yaffs_version, struct super_block *sb,  void  *data, int  silent)         .......          	sb->s_magic = YAFFS_MAGIC; 	sb->s_op = &yaffs_super_ops; 	sb->s_flags |= MS_NOATIME; 	read_only = ((sb->s_flags & MS_RDONLY) != 0 ); 	sb->s_export_op = &yaffs_export_ops;         ....... 	if  (yaffs_parse_options(&options, data_str)) 		 		return  NULL ;         ....... 	sb->s_blocksize = PAGE_CACHE_SIZE; 	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;         ....... 	mtd = get_mtd_device(NULL , MINOR(sb->s_dev));         .......          	if  (!read_only && !(mtd->flags & MTD_WRITEABLE)) { 		read_only = 1 ; 		sb->s_flags |= MS_RDONLY; 	} 	dev = kmalloc(sizeof (struct yaffs_dev), GFP_KERNEL); 	context = kmalloc(sizeof (struct yaffs_linux_context), GFP_KERNEL);         ....... 	memset (dev, 0 , sizeof (struct yaffs_dev)); 	param = &(dev->param); 	memset (context, 0 , sizeof (struct yaffs_linux_context)); 	dev->os_context = context; 	INIT_LIST_HEAD(&(context->context_list)); 	context->dev = dev; 	context->super = sb; 	dev->read_only = read_only; 	sb->s_fs_info = dev; 	dev->driver_context = mtd;                           ............... 	mutex_lock(&yaffs_context_lock); 	for  (mount_id = 0 , found = 0 ; !found; mount_id++) { 		found = 1 ; 		list_for_each(l, &yaffs_context_list) { 			context_iterator =list_entry(l, struct yaffs_linux_context,context_list); 			if  (context_iterator->mount_id == mount_id) 				found = 0 ; 		} 	} 	context->mount_id = mount_id; 	list_add_tail(&(yaffs_dev_to_lc(dev)->context_list), 		      &yaffs_context_list); 	mutex_unlock(&yaffs_context_lock); 	mutex_init(&(yaffs_dev_to_lc(dev)->gross_lock)); 	yaffs_gross_lock(dev);          	err = yaffs_guts_initialise(dev);          	if  (err == YAFFS_OK) yaffs_bg_start(dev); 	if  (!context->bg_thread) 		param->defered_dir_update = 0 ; 	sb->s_maxbytes = yaffs_max_file_size(dev); 	yaffs_gross_unlock(dev); 	 	if  (err == YAFFS_OK) 		inode = yaffs_get_inode(sb, S_IFDIR | 0755 , 0 , yaffs_root(dev));         ....... 	inode->i_op = &yaffs_dir_inode_operations; 	inode->i_fop = &yaffs_dir_operations; 	root = d_alloc_root(inode);         ....... 	sb->s_root = root; 	sb->s_dirt = !dev->is_checkpointed; 	return  sb; } 
小结 本文介绍了Yaffs的架构、文件结构及初始化流程。之后章节介绍:
checkpoint机制 
YAFFS参数调优及Debug 
…….