yaffs基础第一篇——yaffs中的核心数据结构
  ZklfkKGn8hpZ 2023年11月02日 44 0


一个比较大的工程,最最核心的往往是数据结构体的定义,这是我最近一段时间啃yaffs啃出来的体会,一个复杂的函数往往是因为里面包含了各类结构体,结构体成员函数之间的不断转换,因此想要理解好一个大型的软件,一定要深刻的理解它的核心结构体。

话说有一天和trio一起吃饭,trio老师谈到他看代码的习惯,说往往是先不看.c文件,先把头文件仔细读一遍,这点真是深刻啊!

 

我的理解核心数据就像整个代码的经络一样,别看这个程序有多少多少万行代码,它本质上就是在对这些结构体进行操作。废话少说,我们来看一下yaffs的核心结构体。

 

Yaffs的主要的结构体在yaffs_guts.h这个文件中,核心的接头体有:

struct yaffs_DeviceStruct

struct  yaffs_ObjectStruct

Struct yaffs_ObjectHeader

//Struct yaffs_blockinfo

Struct yaffs_spare

Struct yaffs_tags

//Struct yaffs_chunkcache

还有一个比较重要的结构体在yaffs_fs.h这个文件中

Struct  yaffsfs_Handle
 
当然还有其他很多结构体,这里先把主要的列在这儿,我准备从物理上和逻辑上的区分来说明这些结构体的作用,请看下面这张表。
 
结构体名字
属性(物理,逻辑)
作用
yaffs_DeviceStruct
物理
是描述了整个挂载的设备(大小,起始地址,读写操作的函数等等)
yaffs_spare
物理
描述了nandflash每一页后的16个字节
yaffs_ObjectHeader
物理,逻辑
存在于nandflash上,这是物理特性,它的内容是描述了一个文件信息,这是逻辑特性
yaffs_ObjectStruct
逻辑
描述了一个文件的具体信息
yaffsfs_Handle
逻辑
描述了访问文件的信息,读写特性,文件偏移量,文件的yaffs_objectstruct
yaffs_tags
逻辑
它其实是yaffs_spare的一部分,yaffs利用tags中的内容对yaffs进行组织
 
因此可以这么来说实际和nandflash的硬件相关的结构体有三个,其他的结构体都是只是为了维护yaffs的组织目录而在内存中形成的。所以,yaffs的整个目录是可以通过遍历整个yaffs的结构,读取spare区和object headler来得到yaffs中的所有逻辑结构。这部分工作是在yaffs_mount这个函数来实现的,这个我们以后会继续讲到。
下面我们来详细讲解每个结构体:

**************************NO 1*************************************
struct yaffs_DeviceStruct
{
//一下是nandflash的属性,起始地址,结束地址,页大小,是否使用硬件ecc,缓冲的数量等
       int   nBytesPerChunk;  // Should be a power of 2 >= 512
       int   nChunksPerBlock;    // does not need to be a power of 2
       int   startBlock;         // Start block we're allowed to use
       int   endBlock;                 // End block we're allowed to use
       int   nReservedBlocks;     // We want this tuneable so that we can reduce
                                                  // reserved blocks on NOR and RAM.
      
       int   useNANDECC;              // Flag to decide whether or not to use NANDECC
       int   nShortOpCaches;     // If <= 0, then short op caching is disabled, else
                                                 // the number of short op caches (don't use too many).
      
      
       void *genericDevice; // Pointer to device context
                                           // On an mtd this holds the mtd pointer.

       // 以下是nandflash的操作函数,这些是直接和硬件相关,需要我们根据不          同的处理器进行特定的处理,包括读,写,擦除,初始化
      
       int (*writeChunkToNAND)(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare);
       int (*readChunkFromNAND)(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare);
       int (*eraseBlockInNAND)(struct yaffs_DeviceStruct *dev,int blockInNAND);
       int (*initialiseNAND)(struct yaffs_DeviceStruct *dev);
 
       // Runtime parameters. Set up by YAFFS.
       int   internalStartBlock;            // Internal version of startBlock
       int   internalEndBlock;                    // End block we're allowed to use
       int   blockOffset;
       int   chunkOffset;
      
      
       __u16 chunkGroupBits; // 0 for devices <= 32MB. else log2(nchunks) - 16
       __u16 chunkGroupSize; // == 2^^chunkGroupBits
      
 
 
       int isMounted;

//以下是记录整个nandflash所有块信息的部分,在系统初始化的时候会为nandflash所有的块分配一个yaffs_blockinfo的信息结构体,它的首地址就是Blockinfo
 
       yaffs_BlockInfo *blockInfo;
 
       //chunkbits 和 chunkBitmapStride是两个很有意思的东西,正是它们组成了整个nandflash的位图架构,对于一块有32页的nandflash,这里的chunkBitmapStride是4,而chunkbits是8位的,这样刚好4*8=32,也就是每一个位对应了nandflash中一个page,当然在系统挂载初始化的时候会为每一个块都分配,也就是说在首地址是chunkbits中的每一个位都对应了nandflash的一页,当然一个地址对应8页。
       __u8 *chunkBits;   // bitmap of chunks in use
       int   chunkBitmapStride; // Number of bytes of chunkBits per block.
                                                  //   Must be consistent with nChunksPerBlock.
 
 
       int   nErasedBlocks;//记录着器件内所有可供分配的block的数量
       int   allocationBlock;               // 当前分配的block号
       __u32 allocationPage;//记录着上次分配的chunk在block中的序号,每分配一次加1
       int   allocationBlockFinder;      // Used to search for next allocation block
      
       // Runtime state
       int   nTnodesCreated;     
       yaffs_Tnode *freeTnodes;
       int  nFreeTnodes;
       yaffs_TnodeList *allocatedTnodeList;
 
 
       int   nObjectsCreated;
       yaffs_Object *freeObjects;
       int   nFreeObjects;
 
       yaffs_ObjectList *allocatedObjectList;
 
       yaffs_ObjectBucket objectBucket[YAFFS_NOBJECT_BUCKETS];
 
       int      nFreeChunks;
             
       int   currentDirtyChecker; // Used to find current dirtiest block
      
      
       // Operations since mount
       int nPageWrites;
       int nPageReads;
       int nBlockErasures;
       int nGCCopies;
       int garbageCollections;
       int passiveGarbageCollections;
       int nRetriedWrites;
       int nRetiredBlocks;
       int eccFixed;
       int eccUnfixed;
       int tagsEccFixed;
       int tagsEccUnfixed;
       int nDeletions;
       int nUnmarkedDeletions;
      
       yaffs_Object *rootDir;
       yaffs_Object *lostNFoundDir;
      
       // Buffer areas for storing data to recover from write failures
//     __u8            bufferedData[YAFFS_CHUNKS_PER_BLOCK][YAFFS_BYTES_PER_CHUNK];
//     yaffs_Spare bufferedSpare[YAFFS_CHUNKS_PER_BLOCK];
       int bufferedBlock; // Which block is buffered here?
       int doingBufferedBlockRewrite;
 
       //系统使用的缓冲区,我们刚刚定义了nShortOpCaches,这里会开设nShortOpCaches* YAFFS_BYTES_PER_CHUNK这么大的缓冲区
       yaffs_ChunkCache *srCache;
       int srLastUse;
       int cacheHits;
       // Stuff for background deletion and unlinked files.
       yaffs_Object *unlinkedDir;        // Directory where unlinked and deleted files live.
       yaffs_Object *unlinkedDeletion; // Current file being background deleted.
       int nDeletedFiles;                       // Count of files awaiting deletion;
       int nUnlinkedFiles;                            // Count of unlinked files.
       int nBackgroundDeletions;                // Count of background deletions.     
      
       __u8 *localBuffer;
};
typedef struct yaffs_DeviceStruct yaffs_Device;
这里需要注明的是,不是说一块nandflash对应一个yaffs_device,它只是代表了一个物理分区。你也可以通过设定将一块nandflash(比如64M的有4096页)的前2048页作为分区1,后2048作为分区2
 
 
 
**************************NO 2*************************************
struct  yaffs_ObjectStruct            //逻辑文件的具体信息靠这个结构体
{
//以下8位刚好组成一个字节,节约空间,yaffs中大量使用了这种位定义的方式
__u8 deleted: 1;           // This should only apply to unlinked files.
       __u8 softDeleted: 1;    // it has also been soft deleted
       __u8 unlinked: 1;         // An unlinked file. The file should be in the unlinked pseudo directory.
       __u8 fake:1;                // A fake object has no presence on NAND.
       __u8 renameAllowed:1;
       __u8 unlinkAllowed:1;
       __u8 dirty:1;                // the object needs to be written to flash
       __u8 valid:1;                // When the file system is being loaded up, this
                                                 // object might be created before the data
                                                 // is available (ie. file data records appear before the header).
       __u8 serial;                  // serial number of chunk in NAND. Store here so we don't have to
                                                 // read back the old one to update.
       __u16 sum;                        // sum of the name to speed searching
      
       struct yaffs_DeviceStruct *myDev; // The device I'm on
      
                                                       
       struct list_head hashLink;    // list of objects in this hash bucket
                                                
 
       struct list_head hardLinks; // all the equivalent hard linked objects
                                                        // live on this list
       // directory structure stuff
       struct yaffs_ObjectStruct  *parent;  //my parent directory
       struct list_head siblings;       // siblings in a directory
                                                        // also used for linking up the free list
             
       // chunk number in file, 0代表是yaffs_ObjectHeader,非零是文件数据
       int chunkId;          // where it lives
       int nDataChunks; 
      
       //objectId非常重要,一个文件对应一个objcetId,文件可能会很大,分属在   不同的页(chunk)上,但是每个chunk的最好16个字节中定义了这个页        的objectId,也就是说明了这个页是属于哪个文件的,系统也是通过扫描每  页的objectId来组织文件的。
__u32 objectId;           // the object id value
      
      
       __u32 yst_mode;       // protection
 
#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM
       char shortName[YAFFS_SHORT_NAME_LENGTH+1];
#endif
 
       __u32 inUse;
 
       __u32 yst_uid;          // user ID of owner
       __u32 yst_gid;         // group ID of owner
       __u32 yst_atime;        // time of last access
       __u32 yst_mtime;       // time of last modification
       __u32 yst_ctime;        // time of last change
 
 
       __u32 yst_rdev;     // device stuff for block and char devices
 
      
       yaffs_ObjectType variantType;//表示该对象的类型,是目录、普通文件还是链接文件
      
       yaffs_ObjectVariant variant;//是一个联合体,根据对象类型的不同有不同的解释。
      
};
这里需要注明的是,无论你是文件,还是目录,还是链接在yaffs中都是一个objct,只是它的type不同,当然不同的type的部分属性也是不同的,这部分的处理是通过yaffs_ObjectType variantType; yaffs_ObjectVariant variant;这两个成员来处理的。
 
 
 
 
**************************NO 3*************************************
typedef struct
{
       yaffs_ObjectType type;
 
       // Apply to everything  
       int   parentObjectId;
       __u16 sum__NoLongerUsed;    // checksum of name. Calc this off the name to prevent inconsistencies
       char  name[YAFFS_MAX_NAME_LENGTH + 1];
 
       // Thes following apply to directories, files, symlinks - not hard links
       __u32 yst_mode;  // protection
 
 
       __u32 yst_uid;   // user ID of owner
       __u32 yst_gid;    // group ID of owner
       __u32 yst_atime; // time of last access
       __u32 yst_mtime; // time of last modification
       __u32 yst_ctime; // time of last change
 
 
       // File size  applies to files only
       int fileSize;
             
       // Equivalent object id applies to hard links only.
       int  equivalentObjectId;
      
       // Alias is for symlinks only.
       char alias[YAFFS_MAX_ALIAS_LENGTH + 1];
      
       __u32 yst_rdev;  // device stuff for block and char devices (maj/min)
      
 
       __u32 roomToGrow[12];
 
      
} yaffs_ObjectHeader;
 
注明:可以看到其实yaffs_ObjectHeader;的大量成员都是在yaffs_ObjectStruct中已经存在的了,既然已经有了为什么还要建立一个文件头的结构体呢?这其实又回到了我们文章刚开始的关于逻辑和物理的区分。yaffs_ObjectStruct是逻辑的,它是存在在内存中的,yaffs_ObjectHeader是物理的,直接存放在nandflash中的。
 
一方面来说,这是个鸡生蛋的问题,yaffs_ObjectHeader是鸡,yaffs_ObjectHeader是蛋,一定要先有鸡才会有这个蛋的出现,不然蛋不可能凭空出现的,这里也是一样,yaffs_ObjectStruct只所以能在内存中建立正是通过读取头文件的信息和每一个页最好oob区的信息。当然,我个人理解,其实在组织yaffs_ObjectStruct后,如果永远不断电,头文件是没什么作用的,但由于要断电,所以一定要把更改过后的文件重新update头文件,以确保下次能正确的依靠它组织文件。
 
第二个方面来说:一个文件的存在除了它本来数据信息外不可避免的存在一些组织文件的信息,读写权限,修改时间,大小,类型等等,而有限的oob区中又不能够挤出空间来存放,这样就不可避免的为每个文件创建一个头文件的概念,存放了这个文件的一些属性信息。如何区分是头文件还是数据文件也很方便,在读每一页oob区的时候,如果chunkid是0,那这一页就是这个objectId(objcetId也能在oob区中读到)的头文件,否则就是数据文件。
 
 
**************************NO 4*************************************
typedef struct
{
    __u8  tagByte0;
    __u8  tagByte1;
    __u8  tagByte2;
    __u8  tagByte3;
    __u8  pageStatus;    // set to 0 to delete the chunk
    __u8  blockStatus;
    __u8  tagByte4;
    __u8  tagByte5;
    __u8  ecc1[3];
    __u8  tagByte6;
    __u8  tagByte7;
    __u8  ecc2[3];
} yaffs_Spare;
 
 
typedef struct
{
    unsigned chunkId:20;
    unsigned serialNumber:2;
    unsigned byteCount:10;
    unsigned objectId:18;
    unsigned ecc:12;
    unsigned unusedStuff:2;
} yaffs_Tags;

 

SPARE和TAGS这两个结构体比较简单,从这两个结构体的对应上可以很清楚的看到,其实Tags就是Spare的一部分,Spare是整个OOB区的信息,当中的8个字节的tagbyte就是yaffs使用的,可以说yaffs和其他文件系统不同的就是它针对nandflash这种oob区的特点有效的利用了这种特性。因此我们说yaffs是Flash-Specific File System.

 

 

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  lACxYSWBnk9c   2023年11月02日   61   0   0 jvmidejavaSystemobject
ZklfkKGn8hpZ