全部博文(153)
分类:
2009-11-28 14:52:54
之前的讲的yaffs文件系统是direct型的,也就是在没有操作系统下运行的,甚至可以用于单片机,来管理nand的使用,但是yaffs之所以能成为我们众所周知的yaffs,正是因为linux这颗大树,因此之前脱离了linux讲述yaffs是不能领略yaffs真正的精华和价值。Yaffs也已经打了很多补丁,下面解释的都是以android 1.5中带的yaffs2(也包含了yaffs)为例。
1. yaffs如何作为一种文件系统的格式注册进内核?
和所有普通的内核源码(或者是驱动)一样,它可以以模块的形式编译,因为在我们的主文件yaffs_fs.c中必然有一个__init函数,和__exit函数。文件系统的注册和销毁就在这两个函数中,具体代码如下:
static struct file_system_to_install fs_to_install[] = {
//#ifdef CONFIG_YAFFS_YAFFS1
{&yaffs_fs_type, 0},
//#endif
//#ifdef CONFIG_YAFFS_YAFFS2
{&yaffs2_fs_type, 0},
//#endif
{NULL, 0}
};
fsinst = fs_to_install;
register_filesystem(fsinst->fst);
因此可以看到其实yaffs注册了两种文件系统,一种是yaffs,另外一种是yaffs2。Linux的文件系统注册函数就是把yaffs_fs_type(2)这个结构体注册进去。这个结构体的成员很少:
static struct file_system_type yaffs_fs_type = {
.owner = THIS_MODULE,
.name = "yaffs",
.get_sb = yaffs_read_super,
.kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV,
};
而且最最重要的就是get_sb函数,它完成了整个yaffs文件系统和linux vfs挂钩,和nand硬件的挂钩,以及yaffs文件系统初始化。因此理解linux中的yaffs,核心就是理解yaffs_read_super这个函数。
2. yaffs_read_super函数分析1——yaffs文件系统和linux vfs层的挂钩
我们一直提到的linux有很多其他高级操作系统所不及的优势,其中一个很重要的就是linux支持非常广泛的文件系统,几乎地球上通用的文件系统类型linux都能支持。这一切都是通过linux的一种特殊机制vfs(虚拟文件系统)实现的,通过vfs层的转换,可以使linux支持多个不同的文件系统,每个文件系统表示一个vfs的通用接口。因此如果我要为vfs新增一个文件系统(比如yaffs),我们就要为其挂钩我们文件系统 相应的操作。
对于vfs来说,每一个文件系统模型是由一些数据结构对象组成的,主要包括:
超级块数据结构对象——super_block,对具体文件系统的超级块是文件系统中最重要的数据结构,它用来描述整个文件系统信息(组织结构和管理信息)。不涉及文件系统的内容。VFS超级块是各种具体文件系统在安装时建立的,并在这些文件系统卸载时自动删除。VFS超级块实际上应该说成是某个具体文件系统的VFS超级块。超级块对象由super_block结构组成。
索引节点对象(inode,即I节点)——文件系统处理文件所需的信息。索引节点对文件是唯一的。具体文件系统的索引节点存储在磁盘上,使用的时候,必须调入内存,填写VFS的索引节点。所以VFS的索引节点是动态节点。
目录项(dentry)——每个文件除了有一个索引节点结构外,还有目录项dentry结构。dentry结构代表的是逻辑意义上的文件,在磁盘上没有对应的映象。而inode结构代表的是物理意义上的文件,对于一个具体的文件系统,在磁盘上有对应的映象。一个dentry结构必有一个inode结构,而一个inode可能对应多个dentry结构。由于从磁盘读入一个文件并构造相应的目录项需要花费大量的时间,而在完成对目录项的操作后,可能后面还会用到,所以在内存中要保留它。
3. yaffs_read_super函数分析2——super_block
正如我们上面所说的在所有vfs的数据结构中,super_block是最重要的,那我们来看yaffs是怎么实现这一块的。
sb->s_magic = YAFFS_MAGIC;
sb->s_op = &yaffs_super_ops;//超级块处理函数
sb->s_flags |= MS_NOATIME;
sb->s_blocksize = PAGE_CACHE_SIZE;//注意这个不是nandflash的页大小,还是超级块的大小,在arm中是4K
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
sb->s_fs_info = dev = kmalloc(sizeof(yaffs_Device), GFP_KERNEL);//这一步很重要,在超级块的私有成员中挂上了yaffs_Device类型的dev结构体,其实就是完成了超级块和yaffs的最主要的联系,上面的一些成员都仅仅是一般的联系。这个dev结构体中的信息就多了:
dev->genericDevice = mtd;
dev->name = mtd->name;
/* Set up the memory size parameters.... */
nBlocks = mtd->size / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK);这里对于我们来说应该是4096(
dev->startBlock = 0;
dev->endBlock = nBlocks - 1;
dev->nChunksPerBlock = YAFFS_CHUNKS_PER_BLOCK;//每块有32页
dev->totalBytesPerChunk = YAFFS_BYTES_PER_CHUNK;//每页有512字节
dev->nReservedBlocks = 5;
dev->nShortOpCaches = (options.no_cache) ? 0 : 10;
dev->inbandTags = options.inband_tags;
/* ... and the functions.和nand交换数据的操作函数,分成yaffs2/yaffs两类,每类又会区分内核版本,看来
if (yaffsVersion == 2) {
dev->writeChunkWithTagsToNAND =
nandmtd2_WriteChunkWithTagsToNAND;
dev->readChunkWithTagsFromNAND =
nandmtd2_ReadChunkWithTagsFromNAND;
dev->markNANDBlockBad = nandmtd2_MarkNANDBlockBad;
dev->queryNANDBlock = nandmtd2_QueryNANDBlock;
dev->spareBuffer = YMALLOC(mtd->oobsize);
dev->isYaffs2 = 1;
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
dev->totalBytesPerChunk = mtd->writesize;
dev->nChunksPerBlock = mtd->erasesize / mtd->writesize;
#else
dev->totalBytesPerChunk = mtd->oobblock;
dev->nChunksPerBlock = mtd->erasesize / mtd->oobblock;
#endif
nBlocks = mtd->size / mtd->erasesize;
dev->startBlock = 0;
dev->endBlock = nBlocks - 1;
} else {
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,17))
/* use the MTD interface in yaffs_mtdif1.c */
dev->writeChunkWithTagsToNAND =
nandmtd1_WriteChunkWithTagsToNAND;
dev->readChunkWithTagsFromNAND =
nandmtd1_ReadChunkWithTagsFromNAND;
dev->markNANDBlockBad = nandmtd1_MarkNANDBlockBad;
dev->queryNANDBlock = nandmtd1_QueryNANDBlock;
#else
dev->writeChunkToNAND = nandmtd_WriteChunkToNAND;
dev->readChunkFromNAND = nandmtd_ReadChunkFromNAND;
#endif
dev->isYaffs2 = 0;
}
/* ... and common functions 所有内核版本和yaffs版本都通用的函数*/
dev->eraseBlockInNAND = nandmtd_EraseBlockInNAND;
dev->initialiseNAND = nandmtd_InitialiseNAND;
dev->putSuperFunc = yaffs_MTDPutSuper;
dev->superBlock = (void *)sb;
dev->markSuperBlockDirty = yaffs_MarkSuperBlockDirty;
#ifndef CONFIG_YAFFS_DOES_ECC
dev->useNANDECC = 1;
#endif
#ifdef CONFIG_YAFFS_DISABLE_WIDE_TNODES
dev->wideTnodesDisabled = 1;
#endif
dev->skipCheckpointRead = options.skip_checkpoint_read;
dev->skipCheckpointWrite = options.skip_checkpoint_write;
4. yaffs_read_super函数分析3——inode & entry
分析完了super_block,接下来就是inode和entry就简单了。在执行下面这段代码前会有个yaffs的初始化,这也是yaffs_read_super的重要组成部分之一。为了完整性,这个先不讲,流到讲完和vfs的挂钩后再讲。这里要提到的是完成了初始化后就是我们inode和entry的建立过程了。
/* Create root inode */
if (err == YAFFS_OK)
inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0,
yaffs_Root(dev));//为yaffs的初始化结束后生成的root目录建立inode
if (!inode)
return NULL;
//赋相应的inode操作函数
inode->i_op = &yaffs_dir_inode_operations;
inode->i_fop = &yaffs_dir_operations;
T(YAFFS_TRACE_OS, ("yaffs_read_super: got root inode\n"));
//为已经存在的inode分配一个root目录
root = d_alloc_root(inode);
T(YAFFS_TRACE_OS, ("yaffs_read_super: d_alloc_root done\n"));
if (!root) {
iput(inode);
return NULL;
}
//将root目录挂载在super_block上
sb->s_root = root;
sb->s_dirt = !dev->isCheckpointed;