全部博文(153)
分类:
2009-06-22 12:56:50
一个比较大的工程,最最核心的往往是数据结构体的定义,这是我最近一段时间啃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(比如
**************************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.
写的比较仓促,先贴出来,以后慢慢整理。