一 问题描述

在上电启动优化中发现Linux系统下挂载JFFS2文件系统耗时较长,以128M的NOR FLASH为例,用时接近20秒。后续单板的FLASH容量为256M,时间会更长。如此长的挂载时间,会大增加系统的上电启动时间。希望能对mount功能或JFFS2文件系统做适当优化,将256M FLASH的挂载时间降到3~5秒内,优化时需要同时保证文件系统的可靠性和读写速度,要保证兼容优化前的文件系统。

root@CMM:/$ time mount -t jffs2 /dev/mtdblock1 /FLASH0

real    0m 19.83s

user    0m 0.00s

sys     0m 19.73s

二 问题分析

与磁盘文件系统不同,JFFS2文件系统不在FLASH设备上存储文件系统结构信息,所有的信息都分散在各个数据实体节点之中,在挂载文件系统的时候,需扫描整个Flash设备,从中建立起文件系统在内存中的映像,系统在运行期间,就利用这些内存中的信息进行各种文件操作。这就造成了JFFS2文件系统挂载时间过长,尤其是FLASH比较大,文件比较多的情况下。

擦除块小结(erase block summary)补丁可以提高JFFS2文件系统的挂载速度,它最基本的思想就是用空间来换时间。具体来说,就是将擦除块每个节点的原数据信息写在这个擦除块最后的固定位置,当JFFS2挂载的时候,对每个擦除块只需要读取这个小结节点。同时该补丁具有一定的稳定性和兼容性。

●     稳定性:

If the summary node is missing, maybe because of a system power-down before it could be written to flash, nothing bad happens - JFFS2 just falls back to scanning the whole block as it would have done before.

●     兼容性:

The JFFS2 image produced by sumtool is also usable with previous kernel because the summary node is JFFS2_FEATURE_RWCOMPAT_DELETE.

(当不支持擦除块小结特性的JFFS2文件系统发现了一个属性是JFFS2_FEATURE

_RWCOMPAT_DELETE的节点时,在垃圾回收的时候,该节点可以被删除)

三 原理介绍

在每个擦除块的末尾,有一个8 byte长的jffs2_sum_marker节点,该sum_marker节点记录summary node信息的存储位置,summary node可以由文件系统在写操作的过程中自动产生。在文件系统挂载的时候,jffs2_scan_eraseblock()会去读取每个擦除块的最后8byte,如果验证是有效的sum_marker节点,就会更据sum_marker中的offset偏移量去读取summary node,所有在挂载中需要的信息都存放在summary node节点中,因此就没有必要扫描整个擦除块。如果没有找到有效的sum_marker则按正当的启动方式去扫描整个擦除块。

3.1 存放在flash上的节点

● jffs2_raw_summary

struct jffs2_raw_summary

{

jint16_t magic;      /* A constant magic number. */

jint16_t nodetype;   /* = JFFS2_NODETYPE_SUMMARY */

jint32_t totlen;      /* Total length of this node (inc data,etc) */

jint32_t hdr_crc;    /* CRC checksum */

jint32_t sum_num;   /* number of sum entries */

jint32_t cln_mkr;    /* clean marker size, 0 = no cleanmarker */

jint32_t padded;    /* sum of the size of padding nodes */

jint32_t sum_crc;    /* summary information CRC */

jint32_t node_crc;   /* node CRC */

jint32_t sum[0];     /* inode summary info */

};

● jffs2_sum_inode_flash

struct jffs2_sum_inode_flash

{

jint16_t nodetype;  /* == JFFS2_NODETYPE_INODE */

jint32_t inode;     /* inode number */

jint32_t version;   /* inode version */

jint32_t offset;    /* offset on jeb */

jint32_t totlen;    /* record length */

};

该节点包含在扫描过程中所需的dnode节点必要信息。

● jffs2_sum_dirent_flash

struct jffs2_sum_dirent_flash

{

jint16_t nodetype;  /* == JFFS_NODETYPE_DIRENT */

jint32_t totlen;    /* record length */

jint32_t offset;    /* ofset on jeb */

jint32_t pino;      /* parent inode */

jint32_t version;   /* dirent version */

jint32_t ino;       /* == zero for unlink */

uint8_t nsize;      /* dirent name size */

uint8_t type;       /* dirent type */

uint8_t name[0];    /* dirent name */

};

该节点包含扫描过程所需的dirent节点的必要信息。

● jffs2_sum_marker

struct jffs2_sum_marker

{

jint32_t offset; /* Offset of the summary_node in the jeb */

jint32_t magic; /* Magic number (identifies the sum_marker node)*/

};

通过offset偏移量可以找到summary node节点的存储位置。

● 四种节点之间的关系

jffs2_sum_marker存放在擦除末尾固定的8byte里,通过该结构的offset读取summary node,summary node的sum字段存放sum_inode和sum_dirent节点地址信息。

3.2 存放在内存中的节点

● jffs2_sum_unknown_mem

struct jffs2_sum_unknown_mem

{

union jffs2_sum_mem *next;  /* pointer to the next node */

jint16_t nodetype;          /* node type */

};

是存放在内存中的summary node的公共头部。

● jffs2_sum_inode_mem

struct jffs2_sum_inode_mem

{

union jffs2_sum_mem *next;  /* pointer to the next node */

jint16_t nodetype;         /* == JFFS2_NODETYPE_INODE */

jint32_t inode;            /* inode number */

jint32_t version;          /* inode version */

jint32_t offset;           /* offset on jeb */

jint32_t totlen;           /* record length */

};

存放在flash上和memory中的jffs2_sum_inode节点版本号version的不同之处是,内存中节点的版本号要高些。

● jffs2_sum_dirent_mem

struct jffs2_sum_dirent_mem

{

union jffs2_sum_mem *next; /* pointer to the next node */

jint16_t nodetype;         /* == JFFS_NODETYPE_DIRENT */

jint32_t totlen;           /* record length */

jint32_t offset;           /* ofset on jeb */

jint32_t pino;             /* parent inode */

jint32_t version;          /* dirent version */

jint32_t ino;              /* == zero for unlink */

uint8_t nsize;             /* dirent name size */

uint8_t type;              /* dirent type */

uint8_t name[0];           /* dirent name */

};

存放在flash上和memory中的jffs2_sum_drient节点版本号version的不同之处是,内存中节点的版本号要高些。

● union jffs2_sum_mem

union jffs2_sum_mem

{

struct jffs2_sum_unknown_mem u;

struct jffs2_sum_inode_mem i;

struct jffs2_sum_dirent_mem d;

};

该信息存放一个链表上,主要用来决定节点的类型。

● jffs2_summary

struct jffs2_summary

{

uint32_t sum_size;       /* summary data size */

uint32_t sum_num;         /* number of summary nodes */

uint32_t sum_padded;       /* padded size */

union jffs2_sum_mem *sum_list_head; /* summary node list head*/

union jffs2_sum_mem *sum_list_tail;  /* summary node list tail */

jint32_t *sum_buf;          /* buffer for writing out summary */

};

jffs2_sb_info超级块结构扩展了一个指针,指向jffs2 _summary结构。该结构存储擦除块小结(earse block summary)的必要信息。

● 五种节点之间的关系

3.3 挂载过程

传统的挂载方式和EBS挂载方式不同之处在于扫描方法,如果支持EBS,挂载的时候就去读每个擦除块末尾的8bytes找到jffs2_sum_marker从而获取擦除块小结节点信息,而不是扫描整个擦除块。

● 擦除块存在有效的jffs2_sum_marker

擦除块存在有效的jffs2_sum_marker节点,表示整个擦除块已经写满。jffs2_sum_marker节点有一个offset字段,代表从该擦除块开始的地址偏移的地方存放着擦除块小结的信息。EBS会分配c->sector_size - offset (size of summary info)大小的内存,把flash上该擦除块小结的信息读到内存中,通过校验节点和数据的有效性之后,会根据该擦除块小结信息,像传统方式一样构建jffs2_raw_node_refs, jffs2_full_dirent和jffs2_inode_caches,而不用扫描整个擦除块。

● 擦除块不存在有效的jffs2_sum_marker

擦除块不存在有效的jffs2_sum_marker,表示该擦除块没有写满,应该像传统的方式一样扫描整个擦除块同时收集擦除块小结信息。下面是收集擦除块小结信息的过程:

① JFFS2_NODETYPE_INODE,为该类型的节点分配jffs2_sum_inode_mem结构,同时拷贝必要的节点信息到jffs2_sum_inode_mem,并且增加到sum_list_tail链表中,同时统计sum_size 和sum_num大小。

② JFFS2_NODETYPE_DIRENT,为该类型的节点分配jffs2_sum_dirent_mem结构,同时拷贝必要的节点信息到jffs2_sum_dirent_mem,并且增加到sum_list_tail链表中,同时统计sum_size 和sum_num大小。

③ JFFS2_NODETYPE_CLEANMARKER,不做任何处理,在summary node中有个cln_mkr统计cleankarker大小。

④ JFFS2_NODETYPE_PADDING,增加sum_padded的值。

3.4 文件操作

在为一个新的节点分配空间时,EBS会检查c->nextblock是否有足够的空间分配给该节点以及它的summary。如果有空间就会从目前可以使用的空间(jeb->free_size - (collected summary info) - (new node's summary size))中分配。如果没有足够的空间分配,就会为该擦除块产生summary node信息,并写到预留的flash空间中。

四 方案验证

4.1 验证步骤

● 配置新的内核,支持EBS。

File systems --->

Miscellaneous filesystems  --->

<*> Journalling Flash File System v2 (JFFS2) support

(0)   FFS2 debugging verbosity (0 = quiet, 2 = noisy)

[*]   JFFS2 write-buffering support

[ ]    Verify JFFS2 write-buffer reads

[*]   JFFS2 summary support (EXPERIMENTAL)

[ ]   JFFS2 XATTR support (EXPERIMENTAL)

[ ]   Advanced compression options for JFFS2

● 制作JFFS2镜像文件

mkfs.jffs2 -e 0x20000 -p 0x20000 -d rootdir -o rootdir.jffs2

-d is the directory to use for input files, -o is the output file,-e means the earseblock

size for this flash, -p option means pad the last block to the eraseblock size.

● 为JFFS2镜像文件增加小结信息

sumtool -e 0x20000 -p -i rootdir.jffs2 -o rootdir-sum.jffs2

具体的参数要根据实际情况修改。

● 写文件到FLASH

可以尝试使用nandwrite或flashcp,前者是往NAND FLASH写数据,后者是往NOR FLASH写数据。这些命令可以在mtd-utils-1.1.0中找到,下载地址:

ftp://ftp.infradead.org/pub/mtd-utils/mtd-utils-1.1.0.tar.bz2

4.2 验证结果

No summary

With summary

real

0m 19.83s

0m 0.47s

user

0m 0.00s

0m 0.01s

sys

0m 19.73s

0m 0.46s

从验证结果来看,挂载速度有显著提高。

五 参考文献

1、JFFS2 full summary supporthttp://www.infradead.org/pipermail/linux-mtd/2004-November/010887.html

2、Great jffs2 speeduphttp://www.infradead.org/pipermail/linux-mtd/2005-September/013857.html

3、JFFS2 mount timehttp://mhonarc.axis.se/jffs-dev/msg01763.html

4、Reducing JFFS2 mount timehttp://www.embedded-linux.co.uk/tutorial/jffs2-summary

JFFS2文件系统挂载过程优化的分析报告相关推荐

  1. JFFS2文件系统挂载过程(5)

    上篇博文已经把挂载阶段讲完了,那挂载操作是不是就结束了呢? 答案是否定的,还留下了一些操作给gc线程去完成,这么做的目的是为了让挂载阶段所使用的时间减少,提升用户体验(说白了就是让用户以为已经挂载完成 ...

  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. FatFs文件系统移植过程及中度分析

    FatFs 是一个通用的文件系统(FAT/exFAT)模块,用于在小型嵌入式系统中实现FAT文件系统. FatFs 组件的编写遵循ANSI C(C89),完全分离于磁盘 I/O 层,因此不依赖于硬件平 ...

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

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

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

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

  7. 竞品分析报告的几个错误

    一.没有结论的功能点介绍 最常见的竞品分析方法就是对市场上的领先产品进行一次浏览,逐个写出竞品的功能点及流程,不管使用了整齐的表格或者详实的文字描述,又或者是使用了漂亮 的图形和截图,没有结论的统计是 ...

  8. 对于ARM的启动,系统升级,烧写过程和文件系统等方面的总结分析

    本文所述的ARM的指的是Cortex A系列以及ARM9,ARM11,跑Linux操作系统.对于CortexM系列并不一定完全适用: 谈到ARM以及启动和烧写等方面,首先我们要明确一下几个关键词:Ub ...

  9. Linux系统的各个mount点以及文件系统挂载分析

    分析代码,内核的do_sys_open加入检查点,过滤进程touch的目的是,这样可以简单的在用户态通过touch命令创建文件的方式触发进入此分支. caozilong@caozilong-Vostr ...

最新文章

  1. codeception (4)Yii2下创建Acceptance Tests(验收测试)
  2. Facebook的首席技术官:人工智能已用于内容审核,未来会做更多
  3. matlab去除周期噪声,matlab在空域与频域中去除周期噪声、椒盐噪声的简单应用
  4. python爬虫实例-Python爬虫案例集合
  5. 计算机启动太慢可以设置什么来加速启动速度,电脑开机速度慢怎么办 如何加速电脑开机速度慢【详细介绍】...
  6. css3的新特性transform,transition,animation
  7. 创意赛第二季又来了,PaddleHub人脸关键点检测实现猫脸人嘴特效
  8. MyBatis 实际使用案例-mappers
  9. vmware与windows共享文件夹
  10. VS2012+OpenCV2.4.9+Qt5.3.1环境配置
  11. 基于SSM实现的旅游管理系统【附源码】(毕设)
  12. 苹果用计算机密码,苹果mac修改用户名与密码的方法 苹果电脑如何修改开机密码...
  13. 印度黑客号称世界第一,结果第二天被中国黑客干掉了
  14. sharding-jdbc系列之常见问题(十四)
  15. 学习纯软件开发(如Java/Python...)还是C语言、嵌入式、物联网呢?
  16. ansys添加力矩_Ansys加力矩.doc
  17. 你长痘吗?留下痘印吗?民间秘方
  18. AJAX的概括(异步传输)
  19. KDevelop详细Debug教程
  20. LinkCloud:云计算服务 没有优惠活动就是最好的优惠

热门文章

  1. 3.3-3.9 周记
  2. MySQL学习笔记1(增删查改)
  3. 【Java基础】用LinkedList实现一个简单栈的功能
  4. 步骤菜单使用css3实现
  5. 向左滚动,每次滚动的长度可以设置,然后暂停后继续滚动
  6. java单例模式理解_快速理解Java中的五种单例模式
  7. 服务器改家用系统吗,服务器主机改家用
  8. 计算机科学研究生规划,2019计算机考研备考:计算机科学与技术研究方向及复习规划...
  9. proftpd java_Proftpd:编译安装
  10. MongoDB 分析查询性能