eCos扫盲之eCos内存文件系统
这个是2001年学习的时候弄的,现在都忘到爪哇国了,大家了解一下eCos,还是一个很不错的os,这方面armecos是专家。<br /><br />eCos内存文件系统<br /><br /> eCos提供两种存储方式,由宏CYGPKG_FS_RAM_SIMPLE是否定义来决定采用那种存储方式。<br />1. 简单存储<br />优点:操作方法和存储结构简单。<br />缺点:如果一些文件需要按比例分配,产生的碎片可能会导致一些文件无法得到扩展空间,尽管还有足够的空间;需要malloc()函数。<br />2. 块存储<br />可以通过malloc()分配或根据需要从保留的内存块数组中得到。<br />优点:方便管理;用malloc()分配时,可以分配任何空闲内存以得到正确的大小;不需要malloc()函数。<br />缺点:用malloc()分配时,每块的内存占用描述符要占用内存空间;使用内存块数组,则它将被永久保留。<br /><br />所有的文件和目录都用结点对象来描述;每个ramfs_node结构如下:<br />struct ramfs_node<br />{<br /> mode_t mode; // node type<br /> cyg_ucount32 refcnt; // open file/current dir references<br /> nlink_t nlink; // number of links to this node<br /> size_t size; // size of file in bytes<br /> time_t atime; // last access time<br /> time_t mtime; // last modified time<br /> time_t ctime; // last changed status time<br />#ifdef CYGPKG_FS_RAM_SIMPLE<br /><br /> // The data storage in this case consists of a single<br /> // malloced memory block, together with its size.<br /> <br /> size_t datasize; // size of data block<br /> cyg_uint8 *data; // malloced data buffer<br /><br />#else<br /><br /> // The data storage in this case consists of arrays of pointers<br /> // to data blocks. <br /> <br />#if CYGNUM_RAMFS_BLOCKS_DIRECT > 0<br /> // Directly accessible blocks from the inode.<br /> ramfs_block *direct;<br />#endif<br />#if CYGNUM_RAMFS_BLOCKS_INDIRECT1 > 0<br /> // Single level indirection<br /> ramfs_block **indirect1;<br />#endif<br />#if CYGNUM_RAMFS_BLOCKS_INDIRECT2 > 0<br /> // Two level indirection<br /> ramfs_block ***indirect2;<br />#endif<br /><br />#endif<br /> <br />};<br /><br /> .mode 结点类型,文件或目录<br /> .refcnt引用计数;对文件来讲,每一个open是一次引用;对目录来讲,当成为当前目<br />录或打开进行读操作是一次引用<br /> .nlink链接计数,每个目录对该结点的引用<br /> .size 该结点的数据大小(按字节)<br /> .atime结点最后被访问时间<br /> .mtime结点数据最后被修改时间<br /> .ctime结点状态最后被改变的时间<br /> .数据区:整体或块存储<br /><br />目录入口<br />ramfs_dirent结构包含以下字段:<br />struct ramfs_dirent<br />{<br /> ramfs_node *node; // pointer to node<br /> unsigned int inuse:1, // entry in use?<br /> first:1, // first directory entry fragment?<br /> last:1, // last directory entry fragment?<br /> namelen:8, // bytes in whole name<br /> fraglen:8; // bytes in name fragment<br /> off_t next; // offset of next dirent<br /><br /> // Name fragment, fills rest of entry.<br /> char name;<br />};<br /><br /> .node被该入口引用的结点(出现在每一个目录入口片段)<br /> .inuse 1:在使用,0:空闲<br /> .first 1:这是目录入口的第一个片段<br /> .last 1:这是目录入口的第一个片段<br /> .namelen 文件名的总长度<br /> .fraglen 在该片段存贮的文件名的长度(按字节)<br /> .next 该目录入口的下一个片段<br /> .name 存贮在该入口的文件名的片段<br /><br /> 结点对象与目录入口的关系(以块存储为例):<br />1. 如果一个结点描述的是目录,则其数据区存放的是一系列目录入口,包含“。”、“。。”和其下的子目录的目录入口(指向对应的子目录结点对象)和文件的目录入口(指向对应的文件结点对象)。<br />2. 如果一个结点描述的是文件,则其数据区存放的是其文件内容。其所在目录的目<br /> 录入口中该文件的目录入口指向该结点。<br /><br />假如存在这样一个文件:/mylongdirtest/test1.c;每个目录入口保存8个字符长的名字;<br />则其对应的内存文件系统的结构如图(见下页),每个ramfs_node结点的数据按块存贮;<br /> 以下显示了一个文件最大时的数据块的分布:(一块256字节,缺省配置)<br /><br /> Node<br />~ ~<br />| |<br />| |<br />+------------+<br />| *------+--------> data block 0<br />+-------------+<br />| *------+--------> data block 1<br />+-------------+<br />| *------+--------> data block 2<br />+-------------+<br />| *------+--------> data block 3<br />+-------------+<br />| *------+--------> data block 4<br />+-------------+<br />| *------+--------> data block 5<br />+------------+<br />| *------+--------> data block 6<br />+-------------+<br />| *------+--------> data block 7<br />+-------------+<br />| *------+-----------> +------------+<br />+-------------+ | *------+--------> data block 8<br />| *------+----+ +------------+<br />+-------------+ | | |<br /> | ~ ~<br /> | | |<br /> | +-----------+<br /> | | *------+--------> data block 71<br /> | +-----------+<br /> | <br /> +----->+------------+ +------------+<br /> | *------+---------->| *------+---->data block 72<br /> +------------+ +------------+<br /> | | | |<br /> ~ ~ ~ ~<br /> | | | |<br /> +------------+ +-------------+<br /> | *------+----+ | *------+----> data block 135<br /> +------------+ | +-------------+<br /> |<br /> | +------------+<br /> +-------->| *------+----> data block 4104<br /> +------------+<br /> | |<br /> ~ ~<br /> | |<br /> +------------+<br /> | *------+----> data block 4167<br /> +------------+<br />struct cyg_mtab_entry struct ramfs_node<br /> root<br /> ramfs_fste <br /> <br /><br /><br /><br /> <br /> ramblock <br /> <br /> <br /><br /> longdir <br /> ramfs_dirent<br /><br /> <br /> “.”<br /> <br /> <br /><br /> <br /> ramfs_dirent<br /> <br /> “.”<br /> test1<br /> “..”<br /> <br /><br /><br /><br /> <br /> “..”<br /> <br /><br /> “mylongdi”<br /><br /><br /> struct cyg_file<br /><br /><br /> “test1.c”<br /><br /><br /> &ramfs_fileops “rtest”<br />函数说明:<br />1. block_init:如果定义了文件内存块数组,则将这些内存块组织成一个链表,表头是<br /> block_free_list.<br />2. block_alloc: 如果定义了文件内存块数组,则从block_free_list中分配一块,否则用malloc动态分配;并将该块的数据全部置为零.<br />3. block_free: 如果定义了文件内存块数组,则将其释放到以block_free_list为表头的空闲内存链中;否则用free释放到内存池.<br />4. findbuffer_node( ramfs_node *node, off_t pos, cyg_uint8 **buffer,<br /> size_t *size, cyg_bool alloc) //直接调用findbuffer_direct(块存储)<br /> 在结点node的数据区中找到偏移量为pos的内存区域,如果相应的块不存在且alloc=true,<br /> 则分配一块(当然要受到块的数量的限制)。<br />5. freebuffer_node: 释放一个文件结点的数据区。<br />6.findbuffer_direct( off_t pos, ramfs_block **blocks, int nblocks,<br /> cyg_uint8 **buffer, size_t *size, cyg_bool alloc)<br /> 根据pos计算出块号和块内偏移量,如果相应的块不存在且alloc是true,则分配一块;<br /> blocks保存了当前所有的块,如果blocks[块号]=NULL,说明对应的块未分配,<br /> 块号不能大于nblocks;正确返回时*buffer指向对应块偏移量的地址,*size=块的尺寸-块内 <br /> 偏移量(如alloc=true,即该块剩余空间)。<br />7.alloc_node:为文件结点分配一段内存,并初始化struct ramfs_node.<br />8.free-node: 释放一个文件结点。<br />9.dec_refcnt: 文件结点的引用计数减1,如为0则可能会导致该结点被删除。<br />10. add_direntry( ramfs_node *dir, /*欲添加目录的结点(该结点是一目录)*/<br /> const char *name, /*添加的目录名*/<br /> int namelen, /*添加的目录名的长度*/<br /> ramfs_node *node /*引用的结点*/<br /> )<br /> 在目录dir下添加目录入口(长文件名会添加几个目录入口),引用结点node.<br /> 比如:add_direntry(root,”.”,1,root) :在root结点添加一个目录”.”,引用的结点是其自己;<br /> add_direntry(root,”bar”,3,node): 在root结点添加一个目录”bar”,引用结点node.<br /> 11. find_direntry( ramfs_node *dir, const char *name, int namelen )<br /> 在目录dir的数据区查找名字为name的目录,找到后,返回其第一个目录入口;<br />比如:find_direntry(root,”mylongdirtest”,13):返回root结点下名字是”mylongdi”的目录入口.<br /> 12. del_direntry( ramfs_node *dir, const char *name, int namelen )<br /> 删除目录dir下名字为name的所有目录入口片断。<br /> 13. init_dirsearch ( ramfs_dirsearch *ds,ramfs_node *dir, const char *name)<br /> {<br /> ds->dir = dir;<br /> ds->path = name;<br /> ds->node = dir;<br /> ds->name = name;<br /> ds->namelen = 0;<br /> ds->last = false; <br /> }<br /> 初始化目录查找对象ds;<br /> struct ramfs_dirsearch<br /> {<br /> ramfs_node *dir; // 进行查找的目录<br /> const char *path; // 查找路径<br /> ramfs_node *node; // 找到的结点<br /> const char *name; // 最后用到的名字片段<br /> int namelen; // 名字片段的长度<br /> cyg_bool last; // 路径的最后一个名字片段?<br /> };<br />14. find_entry( ramfs_dirsearch *ds )<br />查找ds.path中下一个目录,并修改 ds->node.<br />比如init_dirsearch(ds,root,”mylongdirtest/test1.c”);<br /> 此时:<br /> ds->dir =root;<br /> ds->path = ”mylongdirtest/test1.c”;<br /> ds->node = root;<br /> ds->name = ”mylongdirtest/test1.c”;<br /> ds->namelen = 0;<br /> ds->last = false; <br />find_entry( ds);<br /> ds->dir =root;<br /> ds->path = ”mylongdirtest/test1.c”;<br /> ds->node =longdir; //见P7图示<br /> ds->name = ”mylongdirtest/test1.c”;<br /> ds->namelen = 13;<br /> ds->last = false; <br />其实,ds->dir、ds->path也要作相应的修改(ds->dir=longdir,ds->path=”test1.c”),<br />只是不在这作,在ramfs_find中修改.<br />10. ramfs_find( ramfs_dirsearch *d )<br /> 查找某个目录或文件<br /> 比如:init_dirsearch(ds,root,”mylongdirtest/test1.c”);<br /> ramfs_find(ds);<br /> 此时ds的内容为:<br /> ds->dir =longdir; //指向要查找的文件结点的父目录<br /> ds->path = ”test1.c”;<br /> ds->node =test1; //见P7图示,指向要查找的文件结点<br /> ds->name = ”mylongdirtest/test1.c”;<br /> ds->namelen =7;<br /> ds->last = true; <br />11. ramfs_mount ( cyg_fstab_entry *fste, cyg_mtab_entry *mte )<br /> 安装内存文件系统<br />12.ramfs_open ( cyg_mtab_entry *mte, cyg_dir dir, const char *name,<br /> int mode, cyg_file *file )<br />为了读或写,打开一个文件;如果文件不存在,则根据mode进行相应处理,比如若mode&O_CREATE,则建一个新的文件;文件的偏移量为零或结尾(若mode&O_APPEND). 会使该文件结点的引用计数refcnt+1,且指针offset指向文件头。<br />比如ramfs_open(mte,root,”mylongdirtest/test1.c”,READ,file),结果见图示;<br />13. ramfs_mkdir ( cyg_mtab_entry *mte, cyg_dir dir, const char *name )<br />创建一个目录,同时会在自己目录下添加“。”“。。”两个目录入口,分别引用结点自己和父目录结点。<br />比如ramfs_mkdir(mte,root,”mydir1/mydir2”):在目录mydir1处创建子目录mydir2,<br />若目录mydir1不存在,则出错。<br />14.ramfs_fo_read (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio)<br />ramfs_fo_write (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio)<br />读和写都是对结构CYG_UIO_TAG进行操作;<br /><br /> struct CYG_UIO_TAG<br />{<br /> struct CYG_IOVEC_TAG *uio_iov; /* pointer to array of iovecs */<br /> int uio_iovcnt; /* number of iovecs in array */<br /> off_t uio_offset; /* offset into file this uio corresponds to */<br /> ssize_t uio_resid; /* residual i/o count */<br /> enum cyg_uio_seg uio_seg**; /* see above */<br /> enum cyg_uio_rw uio_rw; /* see above */<br />};<br /><br />struct CYG_IOVEC_TAG<br />{<br /> void *iov_base; /* Base address. */<br /> ssize_t iov_len; /* Length. */<br />};<br /> 比如当应用程序调用函数read/write对某个内存文件进行读/写操作时,函数read/write填充上述两个结构,然后调用ramfs_fo_read/ramfs_fo_write进行相应的读/写操作;<br /><br /><br />不错,ecos的RAM文件系统是非常好的入门教材,
如果你对文件系统感兴趣,建议不要一开始就看FAT等资料,而是先从ROMFS的源码看起,那个特别简单,而且完全符合UNIX IO标准,程序还特别短,学起来很快。<br /> <br /> 然后看看RAMFS文件系统,它的特点是能读写,支持动态地增加文件节点,里面用到的数据结构非常巧妙,这种把文件和目录看成节点的方法,特别能启发我们的设计思路。<br /> <br /> 我是2007年才看的这两个FS实现源码,当时立即就顿悟了文件系统的实现原理,然后很快就看懂了FAT文件,ecos这方面的一致性很好,所有FS都是一个模子。JFFS2也差不多是这样。<br /> <br /> 如果你想一开始就学习正规标准的FS,那么最好选择<b>《ecos增值包》产品</b>。看到很多人从FAT开始学习文件系统,我感到有些担忧,那一定很痛苦,而且现在的FAT源码质量参差不齐,初学者如果参考一个很不标准正规的代码,那么会对以后使用造成障碍,养成坏习惯再改就太困难了,所以最好一开始就学习ecos高质量的标准FS代码。建议不要学习DOS下的那种文件系统,而是一开始就学习UNIX下的那种。Re
我觉得armecos真是天才..<br />随时随地都能进行推销...<br /><br />哈哈.LS说的对,杨工是个商业奇才。技术好自不必说
re
不知道eCos和VxWorks哪个在产品应用更广泛. 。。。
口水+小狗
页:
[1]