Yaffs文件系统
挂载流程
- 初始化与VFS层的接口:
sb->s_op = &yaffs_super_ops;
inode->i_op = &yaffs_file_inode_operations;
inode->i_fop = &yaffs_file_operations; - 初始化与驱动层的接口:
param->write_chunk_tags_fn = nandmtd2_write_chunk_tags;
param->read_chunk_tags_fn = nandmtd2_read_chunk_tags;
param->bad_block_fn = nandmtd2_mark_block_bad;
param->query_block_fn = nandmtd2_query_block;
param->erase_fn = nandmtd_erase_block;
param->initialise_flash_fn = nandmtd_initialise;
- 初始化yaffs_dev结构:
主要在yaffs_gus_initialise函数完成,具体包含以下部分:
初始化擦除块信息:yaffs_init_blocks
初始化cache和buffer:yaffs_init_tmp_buffers
初始化tnode和objs:yaffs_init_tnode_and_objs
初始化根目录:yaffs_create_initial_dir - yaffs2_checkpt_restroe流程:
-> 基础设施搭建完毕,后面就是扫描falsh内存中建立完整的文件试图,YAFFS2首先尝试从checkpoint中恢复文件系统的信息
-> yaffs2_rd_checkpt_open 为读出checkpoint信息做一些初始化工作,首先创建一个checkpt_buffer,初始化checkpt_block_list数组
-> yaffs2_rd_checkpt_validity_marker读取checkpoint头部信息,并判断是否正确
-> yaffs2_rd_checkpt_dev读取struct yaffs_checkpt_dev结构的信息,用来填充yaffs_dev结构相应的字段,以及flash上各个擦除块yaffs_block_info信息,还有chunk_bits表示flash上各个chunk位图
-> yaffs2_rd_checkpt_validity_marker读出checkpoint尾部信息,并判断是否正确
-> yaffs2_rd_checkpt_sum读出校验和,并和计算的比较是否一致 - yaffs2_scan_backwards流程:
如果上述5个步骤都没有错误,则从checkpoint挂载成功,否则就扫描整个flash
-> 扫描整个flash OOB信息,按照bi->seq_number顺序对block进行排序
-> 其次从seq_number最大的block开始往seq_number减小的方向扫描
-> 最后从physical_chunk_index大的chunk开始,并向减少的方向扫描
-> 读取chunk的tags信息,根据tags.chunk_used判断chunk是空闲的还是被使用:根据tags.chunk_id判断是data_chunk,还是object header
-> 如果是data chunk,分几种情况:
a. 根据tags.obj_id查找散列表,找到object对象,则把tags.chunk_id转转成文件的偏移,拿这个偏移和文件对象的shrink_size加入到对应的tnode_tree中,否则删除该chunk:这个一般发生在正常关闭文件的情况下,也即先扫描到到object header,后扫描文件的data chunk,这种情况文件的实际大小应该由扫描碰到的第一个object header所记录的大小的决定
b. 根据tags.obj_id查到散列表,不存在则创建新的object对象,并插入到散列表中,则把tags.chunk_id转换成文件内的偏移,拿这个偏移和文件对象的shrink_size比较,如果在文件的大小范围内则调用yaffs_put_chunk_in_file把该chunk加入到对应的tnode tree中,否则删除该chunk,这一般发生在非正常文件的情况下,也即先扫描到data chunk,后扫面到文件的object header,这种情况文件大小由扫描到的第一个data chunk决定
-> 如果是object header,分几种情况:
a. object header被扫描到,object散列表中并无对应的文件,那么根据tags.obj_id、tags.extra_obj_type或者tags.oject_type或者tags.obj_id、oh->type创建新的object对象,并使用object header的信息初始化object对象,并根据oh->parent_obj_id创建父object对象,把二者联系起来
b. object header被扫面到,object散列表中已经存在对象的object对象,且对象已经有关联的object header,说明这个object head是过时,所以就直接删除该chunk
打开流程
YAFFS文件的打开主要由yaffs_iget函数完成:
根据yaffs_obj的信息填充inode:
inode->i_size = obj->variant.file_variant.file_size;
indoe->i_ino = obj->obj_id;
如果是普通文件:
inode->i_op = &yaffs_file_inode_operations;
inode->i_fop = &yaffs_file_operations;
inode->i_mapping->a_ops = &yaffs_file_address_operations;
如果是目录文件:
inode->i_op = &yaffs_dir_inode_operations;
inode->i_fop = &yaffs_dir_operations;
读取流程
YAFFS2文件的读取主要由yaffs_readpage函数完成:根据pg->index计算文件内偏移,然后转换成logical chunk index,最后在Tnode tree中找到相应的physical chunk index,读取该chunk的数据到页缓存中
读取的过程涉及到YAFFS2内部的cache,首先检测读取的数据是边界否chunk对齐,大小是否data_bytes_per_chunk对齐,如果边界对齐就是不需要内部的cache,如果有就直接从cache读取,没有对应的cache则分配一个cache。从chunk中读取cache中,然后再从cache到页缓存中
写入流程
YAFFS2文件的写入操作主要由yaffs_file_write函数完成:根据pg->index计算文件内偏移,然后转换成logical chunk index:然后分配空闲chunk,建立logical chunk index和physical chunk index,把页缓存中的写入数据到分配的chunk,并把chunk插汝到tnode tree
写入的流程页涉及到YAFFS内存的cache,首先检测写入的数据是否边界chunk对齐,大小是否data_bytes_per_chunk对齐,如果边界对齐就不需要内部的cache直接从页缓存写入chunk中,否则再cache查找该文件是否有对应的cache,如果有就直接写入cache中,如果cache中的数据达到data_bytes_per_chunk就同步到flash;没有对应的cache则分配一个cache,chunk中读取cache中,然后再把数据写入cache中
删除流程
YAFFS2文件的删除采取软删除主要由yaffs_unlink函数完成,对于不同的文件具体划分:普通文件对应yaffs_del_file函数,目录文件对应yaffs_del_dir函数。普通文件如果对应的有数据即n_data_chunk大于0,则把文件移到unlink目录,并释放内存中tnode tree,in->deleted置1,obj_dirty置1,obj->unlinked置1,并写入一个新的object header,oh->file_szie = 0,chunk留待垃圾回收擦除;如果没有对应 的数据则把文件移到deleted目录,则删除空的tnode tree,最后释放object,并写入一个新的object header,oh->file_szie = 0。目录文件比较简单,最后释放object,并写入一个新的object header表示文件被删除
垃圾回收
- 回收时机
-> 在yafffs_wr_data_obj入口调用yaffs_check_gc进行垃圾回收
-> 在yaffs_bg_thread_fn垃圾回收线程中进行回收,该线程会定时被唤醒,根据剩余空间的多少,定时间隔会动态调整,该线程是非实时的,最后调用yaffs_check_gc进行垃圾回收 - 回收策略
根据空闲擦除块的数量判断空间是否紧张,分为如下两种策略:
->aggressive garbage collection,比较紧急的回收,yaffs_find_gc_block找到符合条件的擦除块条件比较宽裕,具体体现以下两个指标的计算上:
/* 表示擦除块dev->gc_page_in_use的阈值,这个阈值越大擦除块越好选择 */
threshold = dev->param.chunks_per_block;
/* 表示选择擦除块的范围,比较紧急的时候要搜索所有的擦除块找合适 的进行回收 */
iterations = dev->internal_end_block - dev->internal_start_block + 1;
->“leasurely” garbage collection,非紧急的回收,yaffs_find_gc_block找到符合条件的擦除块条件比较严格,具体体现在以下两个指标的计算上:
/* 表示找全脏的,无效chunk的块进行回收 */
threshold = background ? (dev->gc_not_done + 2) * 2 : 0;
/* 表示只在很小的范围内搜索这样的擦除块 */
iterations = (dev->internal_end_block - dev->internal_start_block + 1) / 16 + 1;
- 回收过程
-> 首先选择合适进行回收的擦除块,由yaffs_find_gc_block来完成,当block不包含shrink flag时,则它适合gc;如果擦除块包含shrink flag标志,则已完成chunk分配且最早被使用的block合适gc
-> 如果该擦除块还有有效的chunk,则要将正使用的chunk挪到空闲的block上,来腾出这个比较脏的block,由yaffs_gc_process_chunk完成
-> 当整个擦除块无有效chunk时,yaffs_block_became_dirty调用yaffs_erase_block把该擦除块擦除掉