第一章 认识linux文件系统与flash之间的交互流程
1、概述
总所周知,对于linux系统来说,上层采用VFS对所有文件系统的操作进行抽象,屏蔽了底层不同文件系统之间的实现差异。本文主要针对常用的闪存文件系统jffs2,与各位一起探讨下它的实现原理。
2、关键数据结构
2.1 文件系统基本功能
文件系统基本功能主要分为如下两种:
一、是将flash内block数据映射到内存中,供高软读写;
二、是将文件系统数据写入到flash中实现持久化存储;
2.2 常见jffs2数据结构
2.2.1 flash数据实体内核描述符jffs2_raw_node_ref
通常jffs2文件系统最小描述实体用struct jffs2_raw_node_ref进行描述.
struct jffs2_raw_node_ref {
struct jffs2_raw_node_ref *next_in_info;
struct jffs2_raw_node_ref *next_phy;
uint32_t flash_offset;
uint32_t totlen;
};
其中:
1、一个文件的多个jffs2_raw_node_ref信息存储在next_in_info中,组成一个循环链表。
2、链表头指向ffs2_inode_cache的nodes域;链表尾则指向ffs2_inode_cache。因此对于文件系统来说,可以从链表的任意一个节点,开始遍历,将文件的所有信息从flash中读出。
3、flash_offset则表示在flash中的地址偏移。
4、一个flash的earse_block的数据描述实体则由next_phy进行描述。其首位指针分别指向jffs2_earseblock的first_node和last_node。
2.2.2 文件实体内核描述符jffs2_inode_cache
该数据结构体用以描述文件与数据之间的映射关系。每个文件在内核中用jffs2_inode_cache进行描述。
struct jffs2_inode_cache {
struct jffs2_full_dirent *scan_dents;
struct jffs2_inode_cache *next;
jffs2_raw_node_ref *nodes;
uint32_t ino;
int32_t nlink;
int32_t state;
}
其中:
1、 ino为当前文件在文件系统中的唯一索引号,该索引会和inode节点信息中的ino关联起来;具体取值在jffs2_add_ino_cache函数中实现;
2、scan_dents存放jffs2_raw_dirent的上层结构链表临时地址,扫描完后置为NULL;
3、nlink为硬链接个数,在文件系统挂在时会计算指向当前文件的目录项个数;
4、next则指向下个文件的描述信息;
5、nodes内则是每个文件的实际离散数据实体;
从下图中我们可以明显看出jffs2_inode_cache与jffs2_raw_node_ref之间的关系
从中可以看出所有的文件实体描述符都存在一个hash表inocache_list中。并通过唯一标识ino找到对应的jffs2_inode_cache,再依次遍历jffs2_raw_node_ref找到文件对应的所有数据信息。
2.2.3 jffs2_raw_node_ref如何指向jffs2_inode_cache
从上图中我们可以看到任意一个数据实体,都可以找到它对应地文件信息,但是图中指向的确是NULL。不知道各位读者对这个是否存在疑惑。让我们来看三段代码
1、内核描述符初始化
int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri)
{
...
//写文件初始化时,将指向nodes自己
f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
...
}
2、内核描述符与数据实体关联
struct jffs2_raw_node_ref *jffs2_link_node_ref(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t ofs, uint32_t len, struct jffs2_inode_cache *ic)
{
...
jeb->last_node = ref;
if (ic) {
ref->next_in_ino = ic->nodes;
ic->nodes = ref;
} else {
ref->next_in_ino = NULL;
}
...
}
3、通过数据实体描述符指向内核描述符
static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_node_ref *raw)
{
while(raw->next_in_ino) {
raw = raw->next_in_ino;
}
/* NB. This can be a jffs2_xattr_datum or jffs2_xattr_ref and
not actually a jffs2_inode_cache. Check ->class */
return ((struct jffs2_inode_cache *)raw);
}
通过上述代码,我们可以清晰的看出整个文件的构建方法。(当前参考源码来自linux6.8.0)