上篇博文已经把挂载阶段讲完了,那挂载操作是不是就结束了呢?
答案是否定的,还留下了一些操作给gc线程去完成,这么做的目的是为了让挂载阶段所使用的时间减少,提升用户体验(说白了就是让用户以为已经挂载完成了,实际上还有点无伤大雅的小问题需要gc线程处理一下)。
那么纠结gc线程完成了什么挂载遗留下来的任务呢?

这里省略gc线程的前面部分内容,直接到关于完成挂载工作最后部分的代码处:jffs2_garbage_collect_thread-> jffs2_garbage_collect_pass

gc线程起来之后,首先要做的是对挂载过程中未校验的结点完成crc校验工作。直到c->unchecked_size为0

/* jffs2_garbage_collect_pass* Make a single attempt to progress GC. Move one node, and possibly* start erasing one eraseblock.*/
int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
{struct jffs2_inode_info *f;struct jffs2_inode_cache *ic;struct jffs2_eraseblock *jeb;struct jffs2_raw_node_ref *raw;uint32_t gcblock_dirty;int ret = 0, inum, nlink;int xattr = 0;if (mutex_lock_interruptible(&c->alloc_sem))return -EINTR;for (;;) { //进入死循环spin_lock(&c->erase_completion_lock);if (!c->unchecked_size) //如果unchecked_size为0,则退出循环break;/* We can't start doing GC yet. We haven't finished checkingthe node CRCs etc. Do it now. *//* checked_ino is protected by the alloc_sem */if (c->checked_ino > c->highest_ino && xattr) {pr_crit("Checked all inodes but still 0x%x bytes of unchecked space?\n",c->unchecked_size);jffs2_dbg_dump_block_lists_nolock(c);spin_unlock(&c->erase_completion_lock);mutex_unlock(&c->alloc_sem);return -ENOSPC;}spin_unlock(&c->erase_completion_lock);if (!xattr)xattr = jffs2_verify_xattr(c);spin_lock(&c->inocache_lock);ic = jffs2_get_ino_cache(c, c->checked_ino++);//从头开始一个个ino号进行检查,首先获取jffs2_inode_cache实例if (!ic) { //如果获取不到,可能是因为flash上没有该inode号的结点,跳过,继续检查下一个ino号spin_unlock(&c->inocache_lock);continue;}if (!ic->pino_nlink) { //文件硬链接为0或目录被删除,无需检查jffs2_dbg(1, "Skipping check of ino #%d with nlink/pino zero\n",ic->ino);spin_unlock(&c->inocache_lock);jffs2_xattr_delete_inode(c, ic);continue;}switch(ic->state) {case INO_STATE_CHECKEDABSENT:case INO_STATE_PRESENT:jffs2_dbg(1, "Skipping ino #%u already checked\n",ic->ino);spin_unlock(&c->inocache_lock);continue;case INO_STATE_GC:case INO_STATE_CHECKING:pr_warn("Inode #%u is in state %d during CRC check phase!\n",ic->ino, ic->state);spin_unlock(&c->inocache_lock);BUG();case INO_STATE_READING:/* We need to wait for it to finish, lest we move onand trigger the BUG() above while we haven't yetfinished checking all its nodes */jffs2_dbg(1, "Waiting for ino #%u to finish reading\n",ic->ino);/* We need to come back again for the _same_ inode. We'vemade no progress in this case, but that should be OK */c->checked_ino--;mutex_unlock(&c->alloc_sem);sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);return 0;default:BUG();case INO_STATE_UNCHECKED: //一般inode结点是这个状态;}ic->state = INO_STATE_CHECKING;spin_unlock(&c->inocache_lock);jffs2_dbg(1, "%s(): triggering inode scan of ino#%u\n",__func__, ic->ino);ret = jffs2_do_crccheck_inode(c, ic); //主要的检查工作交给jffs2_do_crccheck_inodeif (ret)pr_warn("Returned error for crccheck of ino #%u. Expect badness...\n",ic->ino);jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT); //检查完成后,将状态改为INO_STATE_CHECKEDABSENTmutex_unlock(&c->alloc_sem);return ret;}
...
}

下面接着来看下jffs2_do_crccheck_inode函数。首先为每个jffs2_inode_cache分配一个jffs2_inode_info结构体,然后将检查工作交给jffs2_do_read_inode_internal函数

int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
{struct jffs2_raw_inode n;struct jffs2_inode_info *f = kzalloc(sizeof(*f), GFP_KERNEL); //分配jffs2_inode_info结构体int ret;if (!f)return -ENOMEM;mutex_init(&f->sem);mutex_lock(&f->sem);f->inocache = ic;ret = jffs2_do_read_inode_internal(c, f, &n); //主要的检查函数是这个mutex_unlock(&f->sem);jffs2_do_clear_inode(c, f);jffs2_xattr_do_crccheck_inode(c, ic);kfree (f);return ret;
}

jffs2_do_read_inode_internal读取文件进而组建tn树,并加入jffs2_inode_info的fragtree中

static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,struct jffs2_inode_info *f,struct jffs2_raw_inode *latest_node)
{struct jffs2_readinode_info rii; //这是一个临时结构体,用于承载inode的相关数据信息uint32_t crc, new_size;size_t retlen;int ret;dbg_readinode("ino #%u pino/nlink is %d\n", f->inocache->ino,f->inocache->pino_nlink);memset(&rii, 0, sizeof(rii));//初始化/* Grab all nodes relevant to this ino */ret = jffs2_get_inode_nodes(c, f, &rii); //这个函数会填充rii,其遍历其ino下所有节点,对不同的节点相应的将jffs2_full_dirent和jffs2_full_dnode加入jffs2_readinode_info中if (ret) {JFFS2_ERROR("cannot read nodes for ino %u, returned error is %d\n", f->inocache->ino, ret);if (f->inocache->state == INO_STATE_READING)jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);return ret;}ret = jffs2_build_inode_fragtree(c, f, &rii); //根据rii数据,构建数据的红黑树便于查找if (ret) {JFFS2_ERROR("Failed to build final fragtree for inode #%u: error %d\n",f->inocache->ino, ret);if (f->inocache->state == INO_STATE_READING)jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);jffs2_free_tmp_dnode_info_list(&rii.tn_root);/* FIXME: We could at least crc-check them all */if (rii.mdata_tn) {jffs2_free_full_dnode(rii.mdata_tn->fn);jffs2_free_tmp_dnode_info(rii.mdata_tn);rii.mdata_tn = NULL;}return ret;}if (rii.mdata_tn) { //这种情况存在于该ino号指向的是一个目录、设备文件if (rii.mdata_tn->fn->raw == rii.latest_ref) {f->metadata = rii.mdata_tn->fn;jffs2_free_tmp_dnode_info(rii.mdata_tn);} else {jffs2_kill_tn(c, rii.mdata_tn);}rii.mdata_tn = NULL;}f->dents = rii.fds;//把full_dirent赋给jffs2_inode_infojffs2_dbg_fragtree_paranoia_check_nolock(f);if (unlikely(!rii.latest_ref)) { //若这个ino没有数据节点/* No data nodes for this inode. */if (f->inocache->ino != 1) {JFFS2_WARNING("no data nodes found for ino #%u\n", f->inocache->ino);if (!rii.fds) {//如果没有子目录,这里可能是文件节点或者没有子目录的目录if (f->inocache->state == INO_STATE_READING)jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);return -EIO;}//否则,该目录的inode节点不存在,为了避免他的目录项丢失,做一个假的inode节点JFFS2_NOTICE("but it has children so we fake some modes for it\n");}//下面建立假的inode节点latest_node->mode = cpu_to_jemode(S_IFDIR|S_IRUGO|S_IWUSR|S_IXUGO);latest_node->version = cpu_to_je32(0);latest_node->atime = latest_node->ctime = latest_node->mtime = cpu_to_je32(0);latest_node->isize = cpu_to_je32(0);latest_node->gid = cpu_to_je16(0);latest_node->uid = cpu_to_je16(0);if (f->inocache->state == INO_STATE_READING)jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);return 0;}ret = jffs2_flash_read(c, ref_offset(rii.latest_ref), sizeof(*latest_node), &retlen, (void *)latest_node);//读取latest_ref节点if (ret || retlen != sizeof(*latest_node)) {JFFS2_ERROR("failed to read from flash: error %d, %zd of %zd bytes read\n",ret, retlen, sizeof(*latest_node));/* FIXME: If this fails, there seems to be a memory leak. Find it. */return ret ? ret : -EIO;}crc = crc32(0, latest_node, sizeof(*latest_node)-8);//crc校验if (crc != je32_to_cpu(latest_node->node_crc)) {JFFS2_ERROR("CRC failed for read_inode of inode %u at physical location 0x%x\n",f->inocache->ino, ref_offset(rii.latest_ref));return -EIO;}switch(jemode_to_cpu(latest_node->mode) & S_IFMT) {//根据不同类别的节点不同处理case S_IFDIR:if (rii.mctime_ver > je32_to_cpu(latest_node->version)) {/* The times in the latest_node are actually older thanmctime in the latest dirent. Cheat. */latest_node->ctime = latest_node->mtime = cpu_to_je32(rii.latest_mctime);}break;case S_IFREG:/* If it was a regular file, truncate it to the latest node's isize */new_size = jffs2_truncate_fragtree(c, &f->fragtree, je32_to_cpu(latest_node->isize));if (new_size != je32_to_cpu(latest_node->isize)) {JFFS2_WARNING("Truncating ino #%u to %d bytes failed because it only had %d bytes to start with!\n",f->inocache->ino, je32_to_cpu(latest_node->isize), new_size);latest_node->isize = cpu_to_je32(new_size);}break;case S_IFLNK:/* Hack to work around broken isize in old symlink code.Remove this when dwmw2 comes to his senses and stopssymlinks from being an entirely gratuitous specialcase. */if (!je32_to_cpu(latest_node->isize))latest_node->isize = latest_node->dsize;if (f->inocache->state != INO_STATE_CHECKING) {/* Symlink's inode data is the target path. Read it and* keep in RAM to facilitate quick follow symlink* operation. */uint32_t csize = je32_to_cpu(latest_node->csize);if (csize > JFFS2_MAX_NAME_LEN)return -ENAMETOOLONG;f->target = kmalloc(csize + 1, GFP_KERNEL);if (!f->target) {JFFS2_ERROR("can't allocate %u bytes of memory for the symlink target path cache\n", csize);return -ENOMEM;}ret = jffs2_flash_read(c, ref_offset(rii.latest_ref) + sizeof(*latest_node),csize, &retlen, (char *)f->target);//软连接节点还需读出软连接指向的位置if (ret || retlen != csize) {if (retlen != csize)ret = -EIO;kfree(f->target);f->target = NULL;return ret;}f->target[csize] = '\0';dbg_readinode("symlink's target '%s' cached\n", f->target);}/* fall through... */case S_IFBLK:case S_IFCHR:/* Certain inode types should have only one data node, and it'skept as the metadata node */if (f->metadata) {JFFS2_ERROR("Argh. Special inode #%u with mode 0%o had metadata node\n",f->inocache->ino, jemode_to_cpu(latest_node->mode));return -EIO;}if (!frag_first(&f->fragtree)) {JFFS2_ERROR("Argh. Special inode #%u with mode 0%o has no fragments\n",f->inocache->ino, jemode_to_cpu(latest_node->mode));return -EIO;}/* ASSERT: f->fraglist != NULL */if (frag_next(frag_first(&f->fragtree))) {JFFS2_ERROR("Argh. Special inode #%u with mode 0x%x had more than one node\n",f->inocache->ino, jemode_to_cpu(latest_node->mode));/* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */return -EIO;}/* OK. We're happy */f->metadata = frag_first(&f->fragtree)->node;jffs2_free_node_frag(frag_first(&f->fragtree));f->fragtree = RB_ROOT;break;}if (f->inocache->state == INO_STATE_READING)jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);return 0;
}

经理了这么多的分析,mount的过程终于分析完了。
能看到这里的小伙伴,一定是非常有毅力的。
值得一提的是,mount的整个流程中,对于数据节点,所做的CRC校验只验了节点头部的CRC值,后面gc的检查会检查数据部分的CRC值。
另外,mount过程前期一次读取一大片flash的数据,而后期gc线程起来做检查,一次只读一小片flash的数据。

JFFS2文件系统挂载过程(5)相关推荐

  1. JFFS2文件系统挂载过程优化的分析报告

    一 问题描述 在上电启动优化中发现Linux系统下挂载JFFS2文件系统耗时较长,以128M的NOR FLASH为例,用时接近20秒.后续单板的FLASH容量为256M,时间会更长.如此长的挂载时间, ...

  2. SigmaStar SSD202 openwrt 系统下ubi根文件系统挂载过程

    关于UBI介绍可以参考官方文档 http://www.linux-mtd.infradead.org/doc/ubifs.html 下面是一张简介图,大概的介绍就是UBIFS依赖kernel UBI子 ...

  3. android文件系统挂载过程,有线挂载Android4.2文件系统

    注意:在挂载Android4.2文件系统调试前,一定要将平板的Android系统中的"设置"功能中的"wifi"功能选择"关闭",如图所示 ...

  4. ZYNQ开机挂载SPI FLASH中的jffs2文件系统

    目录 写在前面 Vivado工程建立 petalinux工程建立 下载程序,开机测试 没成功的话看这里 写在前面   做类嵌入式开发很久了,从51到STM32,Arduino,ESP8266,ESP3 ...

  5. 嵌入式软件开发之------浅析linux根文件系统挂载(九)

    Linux代码版本:linux4.4 导读:前些天拿到供应商的一块arm64开发板,需要对其新CPU进行测试评估.需要将公司自己的系统移植上去测试一些参数.在挂载公司的cpio包的时候,出现解压失败. ...

  6. linux文件系统启动流程,linux 内核启动过程以及挂载android 根文件系统的过程

    转载 作者:汕头大学-黄珠唐 时间:2009 年10 月29 日 主要介绍linux 内核启动过程以及挂载android 根文件系统的过程,以及介绍android 源代码中文件系统部分的浅析. 主要源 ...

  7. 挂载jffs2文件系统遇到的问题

    在板子里面使用如下命令mount -t jffs2 /dev/mtdblock7 /var/dr_bin/挂载文件系统时遇到几个问题,如下: 提示No such device问题 报错提示如下: 1. ...

  8. 3--新唐nuc980 kernel支持jffs2, Jffs2文件系统制作, 内核挂载jffs2, uboot网口设置,uboot支持tftp

    本文目录 1.uboot网口设置 2.内核创建MTD分区,Jffs2文件系统制作 3.内核支持jffs2 4.内核挂载jffs2 首先支持spi nor flash https://blog.csdn ...

  9. Zynq-Linux移植学习笔记之32-SPI FLASH文件系统挂载

    1.背景介绍 板上zynq通过spi-1连接一片SPI FLASH,型号为n25q128a11.示意图如下: 由于之前都是通过EMC挂载NOR FLASH,这是第一次使用SPI FLASH挂载文件系统 ...

最新文章

  1. 第三方支付接口的技术比较研究
  2. [K/3Cloud] KSQL 关联表更新字段Update语法
  3. typedef BOOL(WINAPI *MYFUNC) (HWND,COLORREF,BYTE,DWORD);语句的理解
  4. 利用信号进行进程之间的通信
  5. Android开发之常见面试题Activity跳转生命周期变化
  6. 使用named_mutex实现锁机制
  7. Qt工作笔记-QTableWidget插入QcomboBOx后,如何获取数据
  8. 分布式事务模型--XA Specification
  9. iOS中利用UISearchBar实现搜索
  10. 《CCNP TSHOOT 300-135学习指南》——1.2节结构化故障检测与排除方法
  11. 客观评价golang的优缺点
  12. 【转】在线翻译、词典、离线工具大全
  13. matlab仿真没有synchr,Synchro交通仿真系统分析及应用
  14. php中文网灭绝师太照片,灭绝师太照片欣赏
  15. 为什么装了个visual studio就多了个dvd驱动器?
  16. 这样的生产计划与排产,我看行
  17. 德勤中国持续深化与亚马逊云科技的合作,进一步扩充云技术人才储备
  18. nodebb 安装指南
  19. ”去他丫的北上广,老子要去成都定居了!“一名33岁老码农有话说
  20. Spark 实时处理 总文章

热门文章

  1. Linux基础命令(补充:命令行提示字符加颜色)
  2. 矩阵论笔记(二)——线性变换
  3. 5·29“爱脚日”,双驰“个性化量脚制鞋”以爱之名给你呵护
  4. qtable sorting enable中文是按照什么顺序_漫威电影:22部电影观影顺序(附ZY)
  5. vscode新手注意事项(字体间隔,报错提示波浪线,头文件路径,opencv头文件路径)
  6. 汽车电子EMC试验标准ISO11452
  7. FFmpeg 集成 x265 编译及解码
  8. c语言二级编程实例,二级C语言编程_-实例.doc
  9. WPF企业内训全程实录(上)
  10. linux钉钉-使用playonlinux 可以使用视频会议