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

一 问题描述

在上电启动优化中发现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 support  http://www.infradead.org/pipermail/linux-mtd/2004-November/010887.html

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/383430.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

右移函数(字符串,数组)

右移函数 以上是数组右移&#xff0c;将int换成char 把数组内容改成字符串就行。

关于jffs2文件系统如何掉电保护

JFFS2 是将节点信息保存在内存中 Flash上日志型文件系统的资料&#xff0c;了解到传统的基于闪存转换层&#xff08;FLT&#xff09;的文件系统存在的主要问题&#xff1a; 1. 效率低。因为每次都要把要修改的数据所在擦写块放入内存&#xff0c;产生了许多不必要的读操作&…

c中指针简介

c中指针简介 首先我们来看一下指针的一些基本概念 ![在这里插入图片描述](https://img 而对于指针的应用&#xff0c;平常有一些形式&#xff0c;总结了一下大概有这几种用法 对于以上的几种用法&#xff0c;我依次给出详尽的解释 //这是一个普通的整型变量 1 //首先从P 处开…

判断一个字符串是否另一个字符串的右移后的

首先我们把需要判断的字符串传进来&#xff0c;开辟一块大小为两个字符串的长度总和加1的动态的空间&#xff0c;然后后字符串拷贝函数将一个字符串拷贝到开辟空降中&#xff0c;再将这个字符串再次连接到这块动态的空间中&#xff0c;等于就是将一个字符串拷贝了两遍。然后比较…

登陆后保持环境变量导出

在嵌入式开发中&#xff0c;要保证在系统登录后&#xff0c;导出的环境变量依然有效&#xff0c;需要修改如下文件&#xff1a; /etc/profile export PATH/bin:/sbin:/usr/bin:/usr/sbin export PATH/system/bin:$PATH export LD_LIBRARY_PATH/system/lib export LD_LIBRARY_P…

fasync驱动异步通知机制

fasync简介 编辑异步通知fasync应用于系统调用signal和sigaction函数&#xff0c;简单的说&#xff0c;signal函数就是让一个信号与与一个函数对应&#xff0c;每当接收到这个信号就会调用相应的函数。[1]那么什么是异步通知&#xff1f;异步通知类似于中断的机制&#xff0c;当…

Linux中最常见命令总结

Linux中最常见命令总结 基础命令 命令使用格式 命令名【选项参数】 【操作对象】Ls -a workspace目录命令 Ls 默认显示浏览当前文件目录 -a 显示所有文件&#xff0c;不忽略以点开头的文件 Linux下以.开头的文件是隐藏文件 每个目录下文件的两个特殊目录 . 表示目录自身…

不带头结点的链表基础操作(初始化,增删改查)

链表是什么&#xff1f; **链表是一种物理存储单元上非连续、非顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点&#xff08;链表中每一个元素称为结点&#xff09;组成&#xff0c;结点可以在运行时动态生成。每个结点包括…

fcntl的使用

功能描述&#xff1a;根据文件描述词来操作文件的特性。 #include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd); int fcntl(int fd, int cmd, long arg); int fcntl(int fd, int cmd, struct flock *lock); [描述] fcntl()针对(文件)描述符提供控…

链表面试题1:反转单链表,不带头结点。

三个指针p1,p2,p3&#xff0c;p1指向头结点的前一个结点&#xff0c;也就时指空&#xff0c;p2指向头结点&#xff0c;p3指向头结点下一个结点。 p3指向p2的下一个&#xff0c;让p2指针域指向p1&#xff0c;让p1挪到p2上&#xff0c;再让p2指向p3.

dup/dup2函数的用法

系统调用dup和dup2能够复制文件描述符。dup返回新的文件文件描述符&#xff08;没有用的文件描述符最小的编号&#xff09;。dup2可以让用户指定返回的文件描述符的值&#xff0c;如果需要&#xff0c;则首先接近newfd的值&#xff0c;他通常用来重新打开或者重定向一个文件描述…

链表面试题2:编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前

我们可以&#xff0c;用两个新链表&#xff0c;一个存比基准值大的&#xff0c;另一个存比基准值小的。然后再拼接在一起。 用尾插的方法&#xff0c;首先说小的&#xff0c;创建两个指针&#xff0c;一个头&#xff0c;一个尾&#xff0c;再创建个指针跑链表&#xff0c;扫描…

文件系统缓存dirty_ratio与dirty_background_ratio两个参数区别

这两天在调优数据库性能的过程中需要降低操作系统文件Cache对数据库性能的影响&#xff0c;故调研了一些降低文件系统缓存大小的方法&#xff0c;其中一种是通过修改/proc/sys/vm/dirty_background_ration以及/proc/sys/vm/dirty_ratio两个参数的大小来实现。看了不少相关博文的…

栈和队列的基本操作(栈和队列的区别)

数据结构中的栈与内存中的栈的不同 一、数据结构中的堆栈 在数据结构中的堆栈&#xff0c;实际上堆栈是两种数据结构&#xff1a;堆和栈。堆和栈都是一种数据项按序排列的数据结构。 1.栈就像装数据的桶或箱子 我们先从大家比较熟悉的栈说起吧&#xff0c;它是一种具有后进先…

Linux I/O 调度方法

操作系统的调度有 CPU调度 CPU scheduler IO调度 IO scheduler IO调度器的总体目标是希望让磁头能够总是往一个方向移动,移动到底了再往反方向走,这恰恰就是现实生活中的电梯模型,所以IO调 度器也被叫做电梯. (elevator)而相应的算法也就被叫做电梯算法. 而Linux中I…

编译libcurl

1.下载源码后&#xff0c;执行./buidconf产生configure配置文件 2.通过build.sh来设定configure 配置的参数 #!/bin/sh # export CFLAGS-O3 -w -isystem /home/xuxuequan/Ingenicwork/toolchain/mips-gcc472-glibc216-32bit/mips-linux-gnu/libc/usr/include export CPPFLAGS…

链表面试题3:将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成 的。

链表面试题3&#xff1a;将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成 的。 首先我们的思想是将得一个链表和第二个链表的每个结点进行比较&#xff0c;谁小谁就插入到新链表的最后。 首先我们要判段链表是否为空&#xff0c;…

gcc编译参数-fPIC的一些问题

ppc_85xx-gcc -shared -fPIC liberr.c -o liberr.so-fPIC 作用于编译阶段&#xff0c;告诉编译器产生与位置无关代码(Position-Independent Code)&#xff0c;则产生的代码中&#xff0c;没有绝对地址&#xff0c;全部使用相对地址&#xff0c;故而代码可以被加载器加载到内存的…

双向链表的操作(创建,插入,删除)

双向链表的代码看似复杂&#xff0c;其实很简单&#xff0c;只要画图便可明白&#xff0c; 删除 假如要删除的结点叫pos. pos->prev->nextpos->next; pos->next->prevpos->prev; free(pos);

我使用过的Linux命令之hwclock - 查询和设置硬件时钟

我使用过的Linux命令之hwclock - 查询和设置硬件时钟 本文链接&#xff1a;http://codingstandards.iteye.com/blog/804830 &#xff08;转载请注明出处&#xff09; 用途说明 hwclock命令&#xff0c;与clock命令是同一个命令&#xff0c;主要用来查询和设置硬件时钟&#x…