Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1003607
  • 博文数量: 153
  • 博客积分: 4195
  • 博客等级: 上校
  • 技术积分: 2631
  • 用 户 组: 普通用户
  • 注册时间: 2009-06-22 11:32
文章存档

2012年(7)

2010年(35)

2009年(111)

分类:

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,另外一种是yaffs2Linux的文件系统注册函数就是把yaffs_fs_type2)这个结构体注册进去。这个结构体的成员很少:

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);这里对于我们来说应该是409664Mnand

      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两类,每类又会区分内核版本,看来2.6.18是个milestone,很多地方都看到在这个版本中做了很大修改 */

      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,接下来就是inodeentry就简单了。在执行下面这段代码前会有个yaffs的初始化,这也是yaffs_read_super的重要组成部分之一。为了完整性,这个先不讲,流到讲完和vfs的挂钩后再讲。这里要提到的是完成了初始化后就是我们inodeentry的建立过程了。

       /* 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;

阅读(6719) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~