1. file_system_to_install
fsinst是结构体file_system_to_install类型的指针,指向需要向内核注册(install)的文件系统相关信息。
/* Stuff to handle installation of file systems */
struct file_system_to_install {
struct file_system_type *fst;
int installed;
};
fst是struct file_system_type结构体类型的指针,指向需要向内核文件系统链表结构注册的文件系统的必要信息,其定义在fs.h中。
installed是文件系统是否注册的标识,当fst指向的文件系统向内核注册成功后,该位置为1,否则置为0;
2. file_system_type
下面看一下file_system_type结构提的定义(一下定义来自ray项目android/kernel/include/linux/fs.h)。
struct file_system_type {
const char *name;
int fs_flags;
int (*get_sb) (struct file_system_type *, int,
const char *, void *, struct vfsmount *);
void (*kill_sb) (struct super_block *);
struct module *owner;
struct file_system_type * next;
struct list_head fs_supers;
struct lock_class_key s_lock_key;
struct lock_class_key s_umount_key;
struct lock_class_key i_lock_key;
struct lock_class_key i_mutex_key;
struct lock_class_key i_mutex_dir_key;
struct lock_class_key i_alloc_sem_key;
};
简要介绍一下file_system_type比较重要的几个成员:
owner:指向实现文件系统的模块的指针;
name :文件系统类型名称,如yaffs/yaffs2
fs_flags:文件系统类型标志,如FS_FAT/FS_NTFS…
mount:具体文件系统mount函数的函数指针,当我们执行mount 命令时会调用;
struct file_system_type *next: 用于构建内核file system结构链表,便于插入(insmod)、删除(rmmod)等操作,对于具体文件系统而言,该成员需要置NULL;
fs_supers:具有相同文件系统类型的超级快对象链表的头;
3. fs_to_install
fs_to_install数组是在mount时向内核注册的文件系统列表,我们看一下其定义:
static struct file_system_to_install fs_to_install[] = {
{&yaffs_fs_type, 0},
{&yaffs2_fs_type, 0},
{NULL, 0}
};
static struct file_system_type yaffs_fs_type = {
.owner = THIS_MODULE,
.name = "yaffs",
.mount = yaffs_mount,
.kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV,
};
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,
};
从上述代码可以看出,fs_to_install数组有两个元素,分别为yaffs_fs_type和yaffs2_fs_type,两个文件系统分别对file_system_type结构体的owner,name,mount,kill_sb,fs_flags进行赋值。
4. 目录项对象(dentry)
Linux用数据结构dentry来描述文件系统中与某个文件索引节点相链接的一个目录项(可以是文件,也可以是目录)。每个目录项对象可以处于一下四种状态之一:
1) 空闲状态
处于该状态的目录项对象不包含有效的信息,且还没有被VFS使用。对应的内存区由slab分配器进行处理。
2) 未使用状态
处于该状态的目录项对象当前还没有被内核使用。该对象的引用计数器d_count的值为0,但其d_inode字段仍然指向关联的索引节点。该目录项对象包含有效的信息,但为了在必要时回收,他的内容可能被丢弃。
3) 正在使用状态
处于该状态的目录项对象当前正在被内核使用。该对象的引用计数器d_count的值为正数,其d_inode字段指向关联的索引节点对象。该目录项对象包含有效的信息,并且不能被丢弃。
4) 负状态
与目录项关联的索引节点不复存在,那是因为相应的磁盘索引节点已经被删除,或者因为目录项对象是通过解析一个不存在的文件的路径名创建的。目录项对象的d_inode字段被置为NULL,但该对象仍然被保存在目录项高速缓存中,以便后续对同一文件目录名查找操作能够快速完成。
其定义如下:
struct dentry {
atomic_t d_count;
unsigned int d_flags; /* protected by d_lock */
spinlock_t d_lock; /* per dentry lock */
struct inode *d_inode; /* Where the name belongs to - NULL is
* negative */
/*
* The next three fields are touched by __d_lookup. Place them here
* so they all fit in a 16-byte range, with 16-byte alignment.
*/
struct dentry *d_parent; /* parent directory */
struct qstr d_name;
struct list_head d_lru; /* LRU list */
struct list_head d_child; /* child of parent list */
struct list_head d_subdirs; /* our children */
struct list_head d_alias; /* inode alias list */
unsigned long d_time; /* used by d_revalidate */
struct dentry_operations *d_op;
struct super_block *d_sb; /* The root of the dentry tree */
void *d_fsdata; /* fs-specific data */
struct rcu_head d_rcu;
struct dcookie_struct *d_cookie; /* cookie, if any */
struct hlist_node d_hash; /* lookup hash list */
int d_mounted;
unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* small names */
};
dentry 的主要字段的含义解释如下:
d_flags: 目录项高速缓存标志;
d_count: 目录项对象引用计数器;
d_lock: 保护目录项对象的自旋锁;
d_inode: 与文件名关联的索引节点;
d_parent: 父目录的目录项对象;
d_name: 文件名;
d_lru: 用于未使用目录项链表的指针;
d_child: 对目录而言,用于同一父目录中的目录项链表的指针;
d_subdirs: 对目录而言,子目录项链表的头;
d_alias: 用于与统一索引节点(别名)相关的目录项链表的指针;
d_op: 目录项Operation方法;
d_sb: 文件的超级块对象;
d_fsdata: 依赖于文件系统的数据;
d_rcu: 回收目录项对象时,由RCU描述符使用;
5. dentry_operations
与目录项对象关联的方法(const struct dentry_operations *d_op;)称为目录项操作。这些方法由dentry_operations结构体来描述,该结构的地址存放在d_op字段中。
struct dentry_operations {
int (*d_revalidate)(struct dentry *, struct nameidata *);
int (*d_hash) (struct dentry *, struct qstr *);
int (*d_compare) (struct dentry *, struct qstr *, struct qstr *);
int (*d_delete)(struct dentry *);
void (*d_release)(struct dentry *);
void (*d_iput)(struct dentry *, struct inode *);
};
d_revalidate:把目录项对象转换为一个文件路径名之前,判断该目录项对象是否仍然有效,缺省的VFS函数什么都不做。
d_hash:生成一个散列值,这是用于目录项散列表的,特定于具体文件系统的散列函数。参数dentry标识包含路径分量的目录,参数name指向一个结构,该结构包含要查找的路径名分量以及由散列函数生成的散列值。
d_compare:比较两个文件名。name1应该属于dir所指向的目录。缺省的VFS函数是常用的字符串匹配函数。
d_delete:对当前目录项对象的最后一个索引被删除(d_count变为0)时,调用该方法。缺省的VFS函数什么都不做。
d_release:当要释放一个目录项对象时(放入slab分配器),调用该方法。
d_iput:当一个目录项对象变为负状态(即丢弃它的索引节点)时,带哦用该方法。缺省的VFS函数调用input()释放索引节点对象。
6. 超级块对象(super_block)
对具体文件系统的超级块是文件系统中最重要的数据结构,它用来描述整个文件系统信息(组织结构和管理信息)。不涉及文件系统的内容。VFS超级块是各种具体文件系统在安装时建立的,并在这些文件系统卸载时自动删除。VFS超级块实际上应该说成是某个具体文件系统的VFS超级块。超级块对象由super_block结构组成。
struct super_block {
struct list_head s_list; /* Keep this first */
dev_t s_dev; /* search index; _not_ kdev_t */
unsigned long s_blocksize;
unsigned long s_old_blocksize;
unsigned char s_blocksize_bits;
unsigned char s_dirt;
unsigned long long s_maxbytes; /* Max file size */
struct file_system_type *s_type;
struct super_operations *s_op;
struct dquot_operations *dq_op;
struct quotactl_ops *s_qcop;
struct export_operations *s_export_op;
unsigned long s_flags;
unsigned long s_magic;
struct dentry *s_root;
struct rw_semaphore s_umount;
struct semaphore s_lock;
int s_count;
int s_syncing;
int s_need_sync_fs;
atomic_t s_active;
void *s_security;
struct xattr_handler **s_xattr;
struct list_head s_inodes; /* all inodes */
struct list_head s_dirty; /* dirty inodes */
struct list_head s_io; /* parked for writeback */
struct hlist_head s_anon; /* anonymous dentries for (nfs) exporting */
struct list_head s_files;
struct block_device *s_bdev;
struct list_head s_instances;
struct quota_info s_dquot; /* Diskquota specific options */
int s_frozen;
wait_queue_head_t s_wait_unfrozen;
char s_id[32]; /* Informational name */
void *s_fs_info; /* Filesystem private info */
/*
* The next field is for VFS *only*. No filesystems have any business
* even looking at it. You had been warned.
*/
struct semaphore s_vfs_rename_sem; /* Kludge */
/* Granuality of c/m/atime in ns.
Cannot be worse than a second */
u32 s_time_gran;
};
所有超级块对象都以双向循环链表的形式链接在一起,链表中第一个元素用super_block变量来表示,而超级块对象的s_list字段存放指向链表相邻元素的指针。在yaffs文件系统中s_fs_info指向和该超级块关联的yaffs_dev结构(2.6: sb->s_fs_info points to the struct yaffs_dev associated with this superblock)。
7. 超级块操作(super_operations)
和超级块相关联的方法是超级块操作,这些操作由数据结构super_operations来描述,该结构的起始地址存放在super_block的s_op指针中。每一个具体的文件系统都可以定义自己的超级块操作,当VFS需要带哦用其中一个操作时,如read_inode(),执行路径是:sb->s_op->read_inode(inode);
super_operations数据结构的定义如下:
/*
* NOTE: write_inode, delete_inode, clear_inode, put_inode can be called
* without the big kernel lock held in all filesystems.
*/
struct super_operations {
struct inode *(*alloc_inode)(struct super_block *sb);
void (*destroy_inode)(struct inode *);
void (*read_inode) (struct inode *);
void (*dirty_inode) (struct inode *);
int (*write_inode) (struct inode *, int);
void (*put_inode) (struct inode *);
void (*drop_inode) (struct inode *);
void (*delete_inode) (struct inode *);
void (*put_super) (struct super_block *);
void (*write_super) (struct super_block *);
int (*sync_fs)(struct super_block *sb, int wait);
void (*write_super_lockfs) (struct super_block *);
void (*unlockfs) (struct super_block *);
int (*statfs) (struct super_block *, struct kstatfs *);
int (*remount_fs) (struct super_block *, int *, char *);
void (*clear_inode) (struct inode *);
void (*umount_begin) (struct super_block *);
int (*show_options)(struct seq_file *, struct vfsmount *);
ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t);
ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);
};
alloc_inode(sb)
为索引节点对象分配空间,包括具体文件系统的数据所需要的空间。
destroy_inode(inode)
撤销索引节点对象,包括具体文件系统的数据。
dirty_inode(inode,flags)
当索引节点标记为修改(脏)时调用。Ext3文件系统用它来更新新盘上的文件系统日志。
write_inode(inode,wbc)
用通过传递参数指定的索引节点对象的内容更新一个文件系统的索引节点。索引节点对象的i_ino字段标识所涉及磁盘上文件系统的索引节点。
drop_inode(inode)
在即将撤销索引节点时调用,即:当最后一个用户释放该索引节点时;实现该方法的文件系统通常使用generic_drop_inode()函数,该函数从VFS数据结构中一走对索引节点的每一个索引,如果索引节点不再出现在任何目录中,则调用超级块方法delete_inode将它从文件系统中删除。
evict_inode(inode)
put_super(sb)
释放通过传递的参数指定的超级块对象(因为相应的文件系统被卸载)。
sync_fs(sb,wait)
在清除文件系统来更新磁盘上的具体文件系统数据结构时调用(由如yaffs等日志文件系统使用)。
freeze_fs(sb)
unfreeze_fs(sb)
statfs(dentry,kstatfs)
将文件系统的统计信息返回,填写在buf缓冲区中。
remount_fs(sb)
用新的选项重新挂载文件系统(当某个挂载选项必须被修改是该函数被调用)。
umount_begin(sb)
终端一个挂载操作,因为相应的卸载操作已经开始(只在网络文件系统中使用)。
show_options(seq_file, dentry)
用来显示特定文件系统的选项。
show_devname(seq_file, dentry)
show_path(seq_file, dentry)
show_stats(seq_file, dentry)
quota_read(sb)
限额系统使用该方法从文件系统中读取数据,该文件详细说明了所在文件系统的限制。
quota_write(sb)
限额系统使用该方法将数据写入文件,该文件详细说明了所在文件系统的限制。
bdev_try_to_free_page(sb,page,gfp_t)
nr_cached_objects(sb)
free_cached_objects(sb, int)
以上方法对是对所有可能的文件系统类型均是可用的。具体某一个文件系统,某些方法可能不需要,这是将对应的字段置为NULL即可。
8. 块设备结构(block_device)
块设备是Yaffs操作的设备,块设备驱动程序是Linux块子系统中的最底层组件。每一个块设备都有一个block_device结构的描述符表示.
struct block_device {
dev_t bd_dev; /* not a kdev_t - it's a search key */
struct inode * bd_inode; /* will die */
int bd_openers;
struct semaphore bd_sem; /* open/close mutex */
struct semaphore bd_mount_sem; /* mount mutex */
struct list_head bd_inodes;
void * bd_holder;
int bd_holders;
struct block_device * bd_contains;
unsigned bd_block_size;
struct hd_struct * bd_part;
/* number of times partitions within this device have been opened. */
unsigned bd_part_count;
int bd_invalidated;
struct gendisk * bd_disk;
struct list_head bd_list;
struct backing_dev_info *bd_inode_backing_dev_info;
/*
* Private data. You must have bd_claim'ed the block_device
* to use this. NOTE: bd_claim allows an owner to claim
* the same device multiple times, the owner must take special
* care to not mess up bd_private for that case.
*/
unsigned long bd_private;
};
9.yaffs文件系统参数(yaffs_param)
数据结构yaffs_param由yaffs文件系统设备相关的参数组成,如chunk大小,block大小,起始block和结束block,保留block数等等,其定义如下:
struct yaffs_param {
const YCHAR *name;
/*
* Entry parameters set up way early. Yaffs sets up the rest.
* The structure should be zeroed out before use so that unused
* and default values are zero.
*/
int inband_tags; /* Use unband tags */
u32 total_bytes_per_chunk; /* Should be >= 512, does not need to
be a power of 2 */
int chunks_per_block; /* does not need to be a power of 2 */
int spare_bytes_per_chunk; /* spare area size */
int start_block; /* Start block we're allowed to use */
int end_block; /* End block we're allowed to use */
int n_reserved_blocks; /* Tuneable so that we can reduce
* reserved blocks on NOR and RAM. */
int n_caches; /* If <= 0, then short op caching is disabled,
* else the number of short op caches.
*/
int cache_bypass_aligned; /* If non-zero then bypass the cache for
* aligned writes.
*/
int use_nand_ecc; /* Flag to decide whether or not to use
* NAND driver ECC on data (yaffs1) */
int tags_9bytes; /* Use 9 byte tags */
int no_tags_ecc; /* Flag to decide whether or not to do ECC
* on packed tags (yaffs2) */
int is_yaffs2; /* Use yaffs2 mode on this device */
int empty_lost_n_found; /* Auto-empty lost+found directory on mount */
int refresh_period; /* How often to check for a block refresh */
/* Checkpoint control. Can be set before or after initialisation */
u8 skip_checkpt_rd;
u8 skip_checkpt_wr;
int enable_xattr; /* Enable xattribs */
int max_objects; /*
* Set to limit the number of objects created.
* 0 = no limit.
*/
/* The remove_obj_fn function must be supplied by OS flavours that
* need it.
* yaffs direct uses it to implement the faster readdir.
* Linux uses it to protect the directory during unlocking.
*/
void (*remove_obj_fn) (struct yaffs_obj *obj);
/* Callback to mark the superblock dirty */
void (*sb_dirty_fn) (struct yaffs_dev *dev);
/* Callback to control garbage collection. */
unsigned (*gc_control_fn) (struct yaffs_dev *dev);
/* Debug control flags. Don't use unless you know what you're doing */
int use_header_file_size; /* Flag to determine if we should use
* file sizes from the header */
int disable_lazy_load; /* Disable lazy loading on this device */
int wide_tnodes_disabled; /* Set to disable wide tnodes */
int disable_soft_del; /* yaffs 1 only: Set to disable the use of
* softdeletion. */
int defered_dir_update; /* Set to defer directory updates */
#ifdef CONFIG_YAFFS_AUTO_UNICODE
int auto_unicode;
#endif
int always_check_erased; /* Force chunk erased check always on */
int disable_summary;
};
name:保存mtd设备的名称;
n_reserved_blocks: 设备的保留块,用于后续坏块??
n_caches :该字段决定yaffs文件系统是否使用告诉缓存块;
inband_tags:标志位,默认值为0,即采用OOB(Out Of Band)的方式存储tags,可以通过挂载时指定inband_tags选项改变默认值;
total_bytes_per_chunk:每个chunk总的字节数,该字段的值需要大于等于512,但不必是2的次方数;
chunks_per_block:每个block的chunk数;
spare_bytes_per_chunk:每个chunk包含的OOB区域字节数;
start_block:第一个可以使用的block;
end_block:最后一个可以使用的block;
n_reserved_blocks:为GC保留的block的阀值;
n_caches:高速缓存的个数,YAFFS2为了减少数据的碎片以及提高性能为每个文件的读写操作提供了cache,一般小于10个;
no_tags_ecc:标志为,默认为0,即tags中包含ECC校验信息,可以通过内核配置(CONFIG_YAFFS_DISABLE_TAGS_ECC)改变默认值。
is_yaffs2 :标志位,默认为0,即文件系统版本为yaffs,该字段值会在mount过程中根据条件及mtd设备的writesize进行自动转换。
refresh_period:block刷新周期,刷新目的主要找出最旧的处于YAFFS_BLOCK_STATE_FULL状态的block,供GC作为gc_block使用;
skip_checkpt_rd: 标志位,默认为0,支持读取checkpoint,可以提高挂载速度。可以通过挂载时指定挂载选项no-checkpoint-read或no-checkpoint修改默认值;
skip_checkpt_wr:标志位,默认为0,支持写入checkpoint。可以通过挂载是指定选项no-checkpoint-write或no-checkpoint修改默认值。
wide_tnodes_disabled:标志位,默认为0,采用动态位宽,通过内核配置修改CONFIG_YAFFS_DISABLE_WIDE_TNODES配置项可以采用动态位宽。
10.yaffs tag处理函数(tagger)
yaffs_tags_handler结构由yaffs tag的处理函数指针组成,其定义如下:
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);
};
11.yaffs设备(yaffs_dev)
数据结构struct yaffs_dev描述了yaffs文件系统很多信息,如与MTD层交互的接口、NAND的参数(page大小、block数和分区起始位置等)、文件tnode节点的位宽、所有block的使用情况、所有chunk的bitmap、垃圾回收和checkpoint的运行时信息等。
一个设备只能挂载一个yaffs文件系统,后续的垃圾回收等操作,都是基于该yaffs_dev来做的,yaffs_dev是一个RAM-resident的数据结构。其定义如下:
struct yaffs_dev {
struct yaffs_param param;
struct yaffs_driver drv;
struct yaffs_tags_handler tagger;
/* Context storage. Holds extra OS specific data for this device */
void *os_context;
void *driver_context;
struct list_head dev_list;
/* Runtime parameters. Set up by YAFFS. */
int data_bytes_per_chunk;
/* Non-wide tnode stuff */
u16 chunk_grp_bits; /* Number of bits that need to be resolved if
* the tnodes are not wide enough.
*/
u16 chunk_grp_size; /* == 2^^chunk_grp_bits */
/* Stuff to support wide tnodes */
u32 tnode_width;
u32 tnode_mask;
u32 tnode_size;
/* Stuff for figuring out file offset to chunk conversions */
u32 chunk_shift; /* Shift value */
u32 chunk_div; /* Divisor after shifting: 1 for 2^n sizes */
u32 chunk_mask; /* Mask to use for power-of-2 case */
int is_mounted;
int read_only;
int is_checkpointed;
/* Stuff to support block offsetting to support start block zero */
int internal_start_block;
int internal_end_block;
int block_offset;
int chunk_offset;
/* Runtime checkpointing stuff */
int checkpt_page_seq; /* running sequence number of checkpt pages */
int checkpt_byte_count;
int checkpt_byte_offs;
u8 *checkpt_buffer;
int checkpt_open_write;
int blocks_in_checkpt;
int checkpt_cur_chunk;
int checkpt_cur_block;
int checkpt_next_block;
int *checkpt_block_list;
int checkpt_max_blocks;
u32 checkpt_sum;
u32 checkpt_xor;
int checkpoint_blocks_required; /* Number of blocks needed to store
* current checkpoint set */
/* Block Info */
struct yaffs_block_info *block_info;
u8 *chunk_bits; /* bitmap of chunks in use */
unsigned block_info_alt:1; /* allocated using alternative alloc */
unsigned chunk_bits_alt:1; /* allocated using alternative alloc */
int chunk_bit_stride; /* Number of bytes of chunk_bits per block.
* Must be consistent with chunks_per_block.
*/
int n_erased_blocks;
int alloc_block; /* Current block being allocated off */
u32 alloc_page;
int alloc_block_finder; /* Used to search for next allocation block */
/* Object and Tnode memory management */
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;
/* Garbage collection control */
u32 *gc_cleanup_list; /* objects to delete at the end of a GC. */
u32 n_clean_ups;
unsigned has_pending_prioritised_gc; /* We think this device might
have pending prioritised gcs */
unsigned gc_disable;
unsigned gc_block_finder;
unsigned gc_dirtiest;
unsigned gc_pages_in_use;
unsigned gc_not_done;
unsigned gc_block;
unsigned gc_chunk;
unsigned gc_skip;
struct yaffs_summary_tags *gc_sum_tags;
/* Special directories */
struct yaffs_obj *root_dir;
struct yaffs_obj *lost_n_found;
int buffered_block; /* Which block is buffered here? */
int doing_buffered_block_rewrite;
struct yaffs_cache *cache;
int cache_last_use;
/* Stuff for background deletion and unlinked files. */
struct yaffs_obj *unlinked_dir; /* Directory where unlinked and deleted
files live. */
struct yaffs_obj *del_dir; /* Directory where deleted objects are
sent to disappear. */
struct yaffs_obj *unlinked_deletion; /* Current file being
background deleted. */
int n_deleted_files; /* Count of files awaiting deletion; */
int n_unlinked_files; /* Count of unlinked files. */
int n_bg_deletions; /* Count of background deletions. */
/* Temporary buffer management */
struct yaffs_buffer temp_buffer[YAFFS_N_TEMP_BUFFERS];
int max_temp;
int temp_in_use;
int unmanaged_buffer_allocs;
int unmanaged_buffer_deallocs;
/* yaffs2 runtime stuff */
unsigned seq_number; /* Sequence number of currently
allocating block */
unsigned oldest_dirty_seq;
unsigned oldest_dirty_block;
/* Block refreshing */
int refresh_skip; /* A skip down counter.
* Refresh happens when this gets to zero. */
/* Dirty directory handling */
struct list_head dirty_dirs; /* List of dirty directories */
/* Summary */
int chunks_per_summary;
struct yaffs_summary_tags *sum_tags;
/* Statistics */
u32 n_page_writes;
u32 n_page_reads;
u32 n_erasures;
u32 n_bad_markings;
u32 n_erase_failures;
u32 n_gc_copies;
u32 all_gcs;
u32 passive_gc_count;
u32 oldest_dirty_gc_count;
u32 n_gc_blocks;
u32 bg_gcs;
u32 n_retried_writes;
u32 n_retired_blocks;
u32 n_ecc_fixed;
u32 n_ecc_unfixed;
u32 n_tags_ecc_fixed;
u32 n_tags_ecc_unfixed;
u32 n_deletions;
u32 n_unmarked_deletions;
u32 refresh_count;
u32 cache_hits;
u32 tags_used;
u32 summary_used;
};
struct yaffs_param param: yaffs文件系统和设备相关的参数;
struct yaffs_driver drv: yaffs设备驱动结构,由yaffs文件系统对MTD设备操作的函数指针组成,包括读、写、擦除、标记坏块、坏块检查、驱动初始化和驱动注销等操作MTD设备的接口函数,其初始化是在yaffs_mtd_drv_install()函数中完成。其定义如下:
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);
};
struct yaffs_tags_handler tagger: yaffs文件系统tag相关的处理函数;
void *os_context:指向yaffs_linux_context数据结构的指针,该数据结构存储yaffs的运行环境,定义如下:
struct yaffs_linux_context {
struct list_head context_list; /* List of these we have mounted */
struct yaffs_dev *dev;
struct super_block *super;
struct task_struct *bg_thread; /* Background thread for this device */
int bg_running;
struct mutex gross_lock; /* Gross locking mutex*/
u8 *spare_buffer; /* For mtdif2 use. Don't know the buffer size
* at compile time so we have to allocate it.
*/
struct list_head search_contexts;
struct task_struct *readdir_process;
unsigned mount_id;
int dirty;
};
context_list:通过该字段加入到yaffs_context_list全局链表中;
dev:指向YAFFS文件系统超级块的设备的指针;
super:指向VFS层超级块的指针;
bg_thread: YAFFS2后台来即回收线程的指针;
bg_running:启动和停止垃圾回收线程标志位,1为启动,0为停止;
gross_lock:互斥锁,保护整个超级块关键字段的互斥访问,粒度比较大;
spare_buffer: OOB块的缓冲区;
search_contexts:通过该字段把所有Directory Search Context组成链表;
readdir_process:解决使用NFS思索问题而加入的字段;
mount_id:每个NAND Flash分区挂载Yaffs2都分配不同的ID号;
driver_context: 指向mtd_info结构体的指针,mtd_info是MTD子系统的核心数据结构,主要对底层硬件进行封装;
data_bytes_per_chunk: 每个chunk总的字节数,和前面提到的total_bytes_per_chunk一样;
chunk_grp_bits: 采用静态位宽时超过tnode_width宽度后的位数,采用动态位宽时,其值恒为0;
chunk_grp_size: 由chunk_grp_bits转化而来的大小;
tnode_width: 位宽的mask,主要用于快速获取chunk id号;
tnode_size: YAFFS_NTNODES_LEVEL0节点所占用的内存大小,单位是byte;
chunk_shift:主要用来计算logical chunk index以及logical chunk offset;
chunk_div:作用同chunk_shift,主要用于chunk大小不是2次幂的情况;
chunk_mask:作用同chunk_shift,组合起来计算logical chunk offset;
is_mounted:标志位,文件系统挂载时被置位checkpoint是为提高挂载速度而引入的功能,作用同JFFS2的EBS,以空间换取时间,卸载时通过在NAND Flash上保存文件系统超级块快照,挂载时获取快照信息可以快速还原系统状态。
checkpt_page_seq: 控制checkpoint的写入或者读出时的chunk序列号;
checkpt_byte_count: 写入或读出checkpoint信息的字节数;
checkpt_byte_offs: checkpt_buffer缓冲区的偏移量;
checkpt_buffer:写入或读出checkpoint的缓冲区,大小为data_bytes_per_chunk字节;
checkpt_open_write:标志位,yaffs2_checkpt_open时传入的,决定checkpoint读写属性;
blocks_in_checkpt:chenkpoint使用的block的数量;
checkpt_cur_chunk:当前block已经使用掉的chunk数量;
checkpt_cur_block:当前正在使用的block编;
checkpt_next_block:当前正在使用的下一个block编号;
checkpt_block_list:在读checkpoint时使用,该数组中保留使用的block编号,checkpoint信息读取完毕,根据这个数组保存的block编号,更改block的状态为YAFFS_BLOCK_TATE_ CHECKPOINT,以便卸载时检测到这样状态的block时擦除;
checkpt_max_blocks:checkpoint能使用的block最大数量;
checkpoint_blocks_required:checkpoint信息所需的block数量,看该字段的计算大致就知道checkpoint需要保存文件系统的哪些信息;
chunk_bits:指向擦出块位图的指针,每一位对应一个chunk,置0表示没有被使用,置1表示在使用中;
block_info_alt:标志位,采用kmalloc分配block_info所使用的内存则置为0,采用vmalloc分配的内存则置为1;
chunk_bits_alt:标志位,采用kmalloc分配chunk_bits所使用的内存则置为0,采用vmalloc分配的内存则置为1;
chunk_bit_stride:位宽,擦出块中每一个chunk占一位,总的位宽应等于chunks_per_block,要求按byte对其;
n_erased_blocks:空闲擦出块的数目;
alloc_block:当前正在写入的擦出块在block_info指针数组中的下标;
alloc_page:当前正在写入的擦出块中chunk的序列号;
alloc_block_finder:记录下一个被选择写入的擦出块在block_info指针数组的下标;
block_info:指向yaffs_block_info类型的指针数组,数组每一项表示一块擦除块的统计信息,由yaffs_block_info表示,该信息在运行时只存在于内存中,当YAFFS2被卸载时,该数据当作checkpoint信息被记录下来,在下一次挂载时被读出并恢复,具体定义如下:
struct yaffs_block_info {
int soft_del_pages:10; /* number of soft deleted pages */
int pages_in_use:10; /* number of pages in use */
unsigned block_state:4; /* One of the above block states. */
/* NB use unsigned because enum is sometimes
* an int */
u32 needs_retiring:1; /* Data has failed on this block, */
/*need to get valid data off and retire*/
u32 skip_erased_check:1;/* Skip the erased check on this block */
u32 gc_prioritise:1; /* An ECC check or blank check has failed.
Block should be prioritised for GC */
u32 chunk_error_strikes:3; /* How many times we've had ecc etc
failures on this block and tried to reuse it */
u32 has_summary:1; /* The block has a summary */
u32 has_shrink_hdr:1; /* This block has at least one shrink header */
u32 seq_number; /* block sequence number for yaffs2 */
};
soft_del_pages:在YAFFS2包含两种类型的删除,delete和soft delete。前者用于文件内容的更新,比如修改文件中的部分内容,这时YAFFS2会分配新的chunk,将修改后的内容写入新的chunk中,原来chunk的内容自然就没用了,所有将pages_in_use减1,并修改chunk_bits。后者用于文件的删除,YAFFS2在删除文件时,只是删除该文件在内存中的一些描述结构,而被删除文件所占用的chunk不会立即释放,不会删除文件的内容,在后续的文件系统操作中一般也不会把这些chunk分配出去,直到系统进行垃圾收集时才有选择地释放这些chunk。soft_del_pages就表示soft delete的chunk数目;
pages_in_use:该擦除块中被使用的chunk数目,包括已经被soft delete的chunk;
block_state:该擦除块的状态,比如, YAFFS_BLOCK_STATE_FULL表示该擦除块中所有的chunk已经被分配完,YAFFS_BLOCK_STATE_DIRTY表示该擦除块中所有的chunk已经被delete可以被擦除了,YAFFS_BLOCK_STATE_CHECK POINT表示该擦除块保存的是checkpoint信息,YAFFS_BLOCK_STATE_EMPTY表示空闲的擦除块;
needs_retiring:标志位,chunk_error_strikes次数超过3次以上就会置位,该擦除块需要重新回收擦除;
skip_erased_check:标志位,置0时需要对擦除块的进行检测,一般只检测该擦除块的第一个chunk,置1时跳过对擦除块的检测,可以通过CONFIG_YAFFS__ALWAYS_CHECK_CHUNK_ERASED设置;
gc_prioritise:标志位,该块发生过ECC校验错误或check失败,需要在垃圾回收时优先擦除;
chunk_error_strikes:发生ECC校验错误的次数;
has_shrink_hdr:标志位,置0时表示该擦除块上的存储的文件被没有发生过截断truncate,即文件大小没有发生过变化resize,否则必须在文件的objectheader标识,同时该object header所在擦除块上也标识,即has_shrink_hdr置1;
seq_number:序列号,表示擦除块被使用的先后顺序,序号越小越早被使用,在挂载时起到非常重要的作用;
n_boj:文件系统中已经分配的struct yaffs_obj的数量,计算checkpoint信息时使用;
n_tnodes:文件系统中已经分配的struct yaffs_tnode的数量,计算checkpoint信息时使用;
obj_bucket:struct yaffs_obj对象的散列表,以obj_id为键,便于文件查找和操作;
bucket_finder: 散列表中最短链表的索引;
n_free_chunks:flash中空闲的chunk数量;
allocator:指向struct yaffs_allocator结构的指针,YAFFS2文件系统实现的一个内存分配器,主要为struct yaffs_allocator、struct yaffs_tnode分配高速缓存替代原有的slab,struct yaffs_allocator定义如下:
struct yaffs_allocator {
int n_tnodes_created;
struct yaffs_tnode *free_tnodes;
int n_free_tnodes;
struct yaffs_tnode_list *alloc_tnode_list;
int n_obj_created;
struct list_head free_objs;
int n_free_objects;
struct yaffs_obj_list *allocated_obj_list;
};
n_tnodes_created:统计值,分配过总的struct yaffs_tnode数目;
free_tnodes:指向一块连续的内存区域,这块连续内存被分成YAFFS_ALLOCA TION_NTNODE个struct yaffs_tnode结构大小的内存;
n_free_tnodes:分配器中空闲tnodes的数目;
alloc_tnode_list:把yaffs_tnode连续的内存区域存放到此链表中;
n_obj_created:统计值,分配过总的struct yaffs_obj数目;
free_objs:指向一块连续的内存区域,这块连续内存被分成YAFFS_ALLO CATION_NOBJECTS个struct yaffs_obj结构大小的内存;
n_free_objects:分配器中空闲objects的数目;
allocated_obj_list:把yaffs_obj连续的内存区域存放到此链表中;
这里引入了两个最核心的数据结构struct yaffs_tnode和struct yaffs_obj。
struct yaffs_tnode结构体的定义如下:
/*
* Tnodes form a tree with the tnodes in "levels"
* Levels greater than 0 hold 8 slots which point to other tnodes.
* Those at level 0 hold 16 slots which point to chunks in NAND.
* A maximum level of 8 thust supports files of size up to: 2^(3*MAX_LEVEL+4)
*
* Thus a max level of 8 supports files with up to 2^^28 chunks which gives
* a maximum file size of around 512Gbytees with 2k chunks.
*/
#define YAFFS_NTNODES_LEVEL0 16
#define YAFFS_NTNODES_INTERNAL (YAFFS_NTNODES_LEVEL0 / 2)
/*--------------------------- Tnode -------------------------- */
struct yaffs_tnode {
struct yaffs_tnode *internal[YAFFS_NTNODES_INTERNAL];
};
yaffs_tnode是一个长度为YAFFS_NTNODES_INTERNAL的yaffs_tnode结构的指针数组。据此结构创建的节点树最底层的节点称为叶子节点,中间的是非叶子节点。树的所有节点都是yaffs_tnode结构类型。当节点为非叶节点时,数组中的每个元素都指向下一层子节点;当节点为叶子节点时,该数组拆分为YAFFS_NTNODES_LEVEL0个tnode_width位长的短整数,该短整数就是文件内容在flash 上的存储位置。最底层节点即叶节点中tnode_width的位宽决定了文件系统所能寻址的FLASH最大空间,假设tnode_width=16,即可以表示65 536 个chunk。对于chunk的大小为8KB,这种情况下所能寻址的最大FLASH空间是512MB。 文件系统通过节点树查找文件是非常简单和方便的。中间节点使用的Tnode每组有8个指针,需要3位二进制代码对其进行索引,同时,叶节点使用的Tnode每组有16个指针,所以,需要4 位二进制代码对其进行索引。当节点树结构Tnode刚开始建立时,仅建立最底层Lowest-Level Tnode,当File所配置的Chunk数超过16个时,此Tree会建立一个中间节点internal Tnode,并将其第0 个internal[0]指向原本的Lowest-Level Tnode。当读取的Chunk数愈来愈多时,会一直新增Tnode,且节点树也会越来越高。
Tnode节点树建立了文件内偏移到物理chunk之间的映射,YAFFS2靠logical chunk index 来检索Tnode tree,从而得到physical chunk index。比如说读文件时,使用者给出文件内的offset,YAFFS2根据chunk size计算出该offset在文件的第几个chunk中,即计算出logical chunk index,然后在Tnode tree 中找到相应的physical chunk index。
在yaffs文件系统中,不论是文件、目录还是链接(soft linke 或是hard link),在内存中都是以struct yaffs_obj数据结构来描述,因此yaffs_obj是一个RAM-Resident的数据结构。每一个yaffs_obj都对应到一个yaffs_obj_hdr,而yaffs_obj_hdr则是用以储存此yaffs_Object 的一些属性值,,例如该yaffs_Object 是File 或是Directory,此yaffs_Object 的parentObjectId 为何…等等 。其定义如下:
struct yaffs_obj {
u8 deleted:1; /* This should only apply to unlinked files. */
u8 soft_del:1; /* it has also been soft deleted */
u8 unlinked:1; /* An unlinked file.*/
u8 fake:1; /* A fake object has no presence on NAND. */
u8 rename_allowed:1; /* Some objects cannot be renamed. */
u8 unlink_allowed:1;
u8 dirty:1; /* the object needs to be written to flash */
u8 valid:1; /* When the file system is being loaded up, this
* object might be created before the data
* is available
* ie. file data chunks encountered before
* the header.
*/
u8 lazy_loaded:1; /* This object has been lazy loaded and
* is missing some detail */
u8 defered_free:1; /* Object is removed from NAND, but is
* still in the inode cache.
* Free of object is defered.
* until the inode is released.
*/
u8 being_created:1; /* This object is still being created
* so skip some verification checks. */
u8 is_shadowed:1; /* This object is shadowed on the way
* to being renamed. */
u8 xattr_known:1; /* We know if this has object has xattribs
* or not. */
u8 has_xattr:1; /* This object has xattribs.
* Only valid if xattr_known. */
u8 serial; /* serial number of chunk in NAND.*/
u16 sum; /* sum of the name to speed searching */
struct yaffs_dev *my_dev; /* The device I'm on */
struct list_head hash_link; /* list of objects in hash bucket */
struct list_head hard_links; /* hard linked object chain*/
/* directory structure stuff */
/* also used for linking up the free list */
struct yaffs_obj *parent;
struct list_head siblings;
/* Where's my object header in NAND? */
int hdr_chunk;
int n_data_chunks; /* Number of data chunks for this file. */
u32 obj_id; /* the object id value */
u32 yst_mode;
YCHAR short_name[YAFFS_SHORT_NAME_LENGTH + 1];
#ifdef CONFIG_YAFFS_WINCE
u32 win_ctime[2];
u32 win_mtime[2];
u32 win_atime[2];
#else
u32 yst_uid;
u32 yst_gid;
u32 yst_atime;
u32 yst_mtime;
u32 yst_ctime;
#endif
u32 yst_rdev;
void *my_inode;
enum yaffs_obj_type variant_type;
union yaffs_obj_var variant;
};
下面介绍yaffs_obj中的几个关键字:
deleted,soft_del,ulinked:这三个字段用于描述该文件对象在删除过程中所处的阶段。在删除文件时,首先要将文件从原目录移至一个特殊的系统目录/unlinked,一次拒绝应用程序对该文件的访问,此时将unlinked置为1;然后判断该文件长度是否为0,如果为0,该文件可以直接删除,此时将deleted置为1;如果不为0,将deleted和soft_del都置为1,表明该文件数据所占的chunk还没有释放,要留待后续垃圾回收处理;
fake:创建fake directory时被置为1;有两种目录,一种是普通的目录,有object header,另外一种是fake directory,没有object header,只存在内存中。他们是root,lost_found,deleted域unlinked;
dirty:文件被修改时置为1;
valid:文件object header被写入时置1,没有object header时置为0,表示文件并未真正生效;
hash_link:通过该字段把文件对象挂接到散列表中;
parent:指向父目录的yaffs_obj对象;
siblings:在同一个目录的yaffs_obj对象通过该字段组成双向循环链表;
hdr_chunk:每个文件在flash上都有一个object header,存储着该文件的大小、所有者、创建修改时间等信息,hdr_chunk就是该文件头在flash上的chunk号, object header一旦被写入chunk,就不能再修改,只能在另一个chunk 中写入一个新的object header;
obj_id:每一个文件系统对象都被赋予一个唯一的编号,作为对象标识,也用于将该对象挂入一个散列表,加快对象的搜索速度;
variant_type、variant:前者表示该对象的类型,是目录、普通文件还是链接文件,后者是一个联合体,根据对象类型的不同有不同的解释,双向循环链表, obj->varient. dir_variant.children 记录着该目录下的所有子目录和文件;
gc_cleanup_list:数组,保存垃圾回收时可以删除的yaffs_obj对象obj_id;
n_clean_ups: gc_cleanup_list数组的下标;
has_pending_prioritised_gc:标志位,设备存在优先回收的擦除块;
gc_disable:标志位,置1禁止垃圾回收,置0使能垃圾回收,主要用于垃圾回收时的同步,防止垃圾回收的重入,进入垃圾流程置1,退出时置0;
gc_block_finder:存储可以进行垃圾回收擦除块的编号;
gc_dirtiest:存储最脏的擦除块的编号;
gc_pages_in_use:被选中垃圾回收的擦除块有效数据使用的page数目不能超过一定阈值,否则代价太大,需要把有效数据搬移到空闲擦除的page中;
gc_not_done:跳过垃圾回收的次数;
gc_block:正在被垃圾回收的擦除块;
gc_chunk:垃圾回收时会判断每个chunk是否有效,gc_chunk表示正在被检查的那个chunk,有效的话需要把该chunk的数据搬移到其他空闲擦除块的chunk上;
gc_skip:没有使用该字段;
root_dir、lost_n_found、unlinked_dir、del_dir、unlinked_deletion:这几种目录都是fake directory,只存在于内存中,没有object header,该类目录禁止改名、禁止删除,文件对象的fake的字段置1;
cache:指向yaffs_cache数据结构的指针,当param->n_caches大于0时,在yaffs_guts_initialise()函数中将初始化cache[dev->cache = kmalloc(cache_bytes, GFP_NOFS);]。现实的文件操作中存在大量的short sequential reads, and short sequential writes,页缓存能提高short sequential reads类型的读性能,而为了提高short sequential writes类型的写性能,YAFFS2文件系统实现了自己的文件缓存。yaffs_guts_initialise函数对文件系统要使用的cache进行初始化,缓存的数目有dev->param.n_caches决定,缓存的大小由dev->param.total_ bytes_per_chunk决定,写入的数据小于total_bytes_per_chunk时就暂时缓存到cache中,达到total_bytes_per_chunk再写入到FLASH。 struct yaffs_cache的定义如下:
/* ChunkCache is used for short read/write operations.*/
struct yaffs_cache {
struct yaffs_obj *object;
int chunk_id;
int last_use;
int dirty;
int n_bytes; /* Only valid if the cache is dirty */
int locked; /* Can't push out or flush while locked. */
u8 *data;
};
cache_last_use:cache被使用的次数,通过该字段可以看出文件操作是否存在频繁的short sequential writes;
temp_buffer:YAFFS2实现了一个临时的buffer,buffer大小为total_bytes_per_chunk,主要用来临时存放从chunk中读出的数据;
seq_number:当被用作序列号时,sequnce Number表示擦除块被使用的前后顺序,越小则表示被使用的越早,反之亦然。擦除块的sequnce number在gc与上电scan时起到了非常重要的作用;
oldest_dirty_seq:最小的seq_number号也就是最老的seq_number号;
oldest_dirty_block:最小的seq_number号对应的擦除块编号;
dirty_dirs: 需要同步更新的文件对象挂到这个链表下;
12.yaffs_obj_hdr
struct yaffs_obj_hdr是表示object header的数据结构,该结构只存在与Flash,每个文件都有一个object header,存储着该文件的大小、所有者、创建修改时间等信息。object header一旦被写入chunk,就不能被修改,如果文件被修改,只能在另一个chunk中写入一个新的object header。该结构体的定义如下:
/* This is the object structure as stored on NAND */
struct yaffs_obj_hdr {
enum yaffs_obj_type type;
/* Apply to everything */
int parent_obj_id;
u16 sum_no_longer_used; /* checksum of name. No longer used */
YCHAR name[YAFFS_MAX_NAME_LENGTH + 1];
/* The following apply to all object types except for hard links */
u32 yst_mode; /* protection */
u32 yst_uid;
u32 yst_gid;
u32 yst_atime;
u32 yst_mtime;
u32 yst_ctime;
/* File size applies to files only */
u32 file_size_low;
/* Equivalent object id applies to hard links only. */
int equiv_id;
/* Alias is for symlinks only. */
YCHAR alias[YAFFS_MAX_ALIAS_LENGTH + 1];
u32 yst_rdev; /* stuff for block and char devices (major/min) */
u32 win_ctime[2];
u32 win_atime[2];
u32 win_mtime[2];
u32 inband_shadowed_obj_id;
u32 inband_is_shrink;
u32 file_size_high;
u32 reserved[1];
int shadows_obj; /* This object header shadows the specified object if > 0 */
/* is_shrink applies to object headers written when wemake a hole. */
u32 is_shrink;
};
type: 关联文件的类型,表示该文件是普通文件、目录还是链接(SoftLink/HardLink)文件等;
name:yaffs object的名称;
is_shrink: 标识所关联的文件是否发生过resize。如果文件发生过resize,必须在file的object header上标识出这一点;同事也必须在object header所在的block上也标识出这一点。关于shrink header的说明见[Yaffs] Improving/ignoring yaffs2 shrink header handling http://www.yaffs.net/lurker/message/20070430.103622.962952a8.fr.html
n_obj:文件系统中已经分配的struct yaffs_obj的数量,计算checkpoint信息时使用;
yst_mode:表示yaffs object的权限;
yst_uid:文件的User ID;
yst_gid:文件的Group ID;
yst_atime:文件的最后访问时间;
yst_mtime:文件的最后修改时间;
yst_ctime:文件的创建时间;
file_size_low:文件的大小;
equiv_id:hardlink对应的Object ID;
alias:符号链接的别名;
13.yaffs_ext_tags
struct yaffs_ext_tags {
unsigned chunk_used; /* Status of the chunk: used or unused */
unsigned obj_id; /* If 0 this is not used */
unsigned chunk_id; /* If 0 this is a header, else a data chunk */
unsigned n_bytes; /* Only valid for data chunks */
/* The following stuff only has meaning when we read */
enum yaffs_ecc_result ecc_result;
unsigned block_bad;
/* YAFFS 1 stuff */
unsigned is_deleted; /* The chunk is marked deleted */
unsigned serial_number; /* Yaffs1 2-bit serial number */
/* YAFFS2 stuff */
unsigned seq_number; /* The sequence number of this block */
/* Extra info if this is an object header (YAFFS2 only) */
unsigned extra_available; /* Extra info available if not zero */
unsigned extra_parent_id; /* The parent object */
unsigned extra_is_shrink; /* Is it a shrink header? */
unsigned extra_shadows; /* Does this shadow another object? */
enum yaffs_obj_type extra_obj_type; /* What object type? */
loff_t extra_file_size; /* Length if it is a file */
unsigned extra_equiv_id; /* Equivalent object for a hard link */
};
struct yaffs_ext_tags数据结构是用来表示chunk的归属信息的,其定义如下:
chunk_used:为0表示该chunk未分配,为1表示该chunk已经分配使用;
obj_id: 为0表示该chunk未分配使用,非0表示文件的obj_id信息,通过该字段可以知道该chunk属于哪个文件;
chunk_id: 为0表示该chunk存储的是一个文件的object header;非0表示该chunk存储的是data chunk,表示文件logical chunk index,可以根据这个字段计算所保存的数据在文件内的偏移;
n_bytes:表示该chunk包含的数据量,单位是byte;
seq_number:等同于擦除块的seq_number,表示擦除块被使用的先后顺序,block中所有chunk的该字段是一样的,所以扫描时可以根据此信息对block进行排序;
extra_obj_type:表示该chunk中存储的文件类型, extra_available为1才能使用这个信息;
文章来源:http://kaffeel.org/20130330/yaffs%e6%96%87%e4%bb%b6%e7%b3%bb%e7%bb%9f%e7%9a%84%e9%87%8d%e8%a6%81%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84/