在写这篇文章之前,xxx已写过了几篇关于改文件系统文件主题的文章,想要了解的朋友可以去翻一下之前的文章
RTT的文件系统要重分为三层,如下图:
图1
RTT的用户手册中也有分析到,最上层一套面向嵌入式系统,专门优化过的虚拟文件系统(接口)。通过它,RT-thread操纵系统够能适配上层不同的文件系统格式。接下来是各种文件系统,现在最新的rt-thread1.1.0源码中已包括8种文件系统:devfs,elmfat,jffs2,nfs,romfs,skeleton,uffs,yaffs2,这些文件系统通常是网上的一些开源的目项,RTT移植过去做些改修适配。最低层就是各位体具设备的驱动层了,如SD Card,spi flash等。
这些西东都是比拟理论的西东,上面一起来看看RTT是如何体具实现这些框架框架的。
1 RTT文件系统的用应接口实现(最顶层)
所谓用应层接口其实就是指该以什么样的接口数函式形供给给用户用使,这里的用户就是指程编开发人员,开发者用使这些接口行进文件关相操纵,本根不必心关其部内是如何实现或是数据体具是保存在哪个设备中。
在RTT源码文件.\components\dfs\src目录下的dfs_posix.c源文件就是体具实现这些接口的源码代,其共实现了接口有20个与文件目录关相的posix接口数函:
- chdir 改修作工目录
- close 关闭一个文件
- closedir 关闭一个目录
- fstat 获得一个文件的态状信息
- getcwd 获得以后的作工目录
- lseek 移动文件滑标操纵
- mkdir 建新一个目录
- open 打开一个文件
- opendir 打开一个目录
- read 从一个打开的文件中读取数据
- readdir 读取下一个目录节点
- rename 重定名一个文件
- rewinddir 重置目录流
- rmdir 删除一个目录
- seekdir 与lseek相似,这里操纵的是目录,设置目录滑标的位置
- stat 获得文件关相信息
- statfs 获得一个挂载的文件系统关相信息
- telldir 获得以后目录流中滑标的位置
- unlink 将一个目录从文件系统中移除
- write 将数据入写文件
RTT文件系统的顶层用应接口源码文件除了dfs_posix.c这一接口实现外之,还包括dfs.c,dfs_fs.c,dfs_file.c,这几个源文件,这几个源文件的关系如下图所示:
图中的箭头表现调用的关系,即dfs_posix.c源文件中的一些接口数函实现会调用底下dfs.c,dfs_fs,c,dfs_file.c源文件中的一些数函。
从上图可以看出,dfs_posix.c源文件是RTT文件系统最顶层面向用使者的一个接口数函实现文件,它的体具实现是赖依上层三个源文件来实现的。
从上图也可以看出,RTT将文件系统的顶层源文件都以dfsxxx的式形定名,这个dfs时暂看成是directory,file,system三个英文的首字母缩写吧,这里简称为DFS架框。
2 DFS架框
2.1 为什么会有DFS架框?
RTT为了够能持支各种文件系统,在顶层计设了这么一个架框,使各个开源的文件系统够能经过略微改修匹配到这个架框就可以实现移植。
2.2 DFS架框的成组容内
DFS架框部内要重包括三个表:filesystem_operation_table,filesystem_table,fd_table,以及一个文件系统互斥锁fslock用于决解资源冲突。
- filesystem_operation_table: 这个表个一每表项表现一个文件系统对应的一套操纵数函及关相属性。
- filesystem_table: 这个表录记着挂载的文件系统,即个一每表项表现挂载的一个文件系统。
- fd_table: 这个表录记着以后打开的文件句柄。
注:往filesystem_operation_table添加一个文件系统的操纵集操纵叫“册注”,往filesystem_table添加一个文件系统的操纵叫"挂载",往fd_table表内添加一个文件句柄的操纵为通普添加录记操纵。
2.2.1 filesystem_operation_table
filesystem_operation_table是录记各个文件系统操纵接口的表,其各个表项的结构如下义定:
/* File system operations struct */ struct dfs_filesystem_operation { char *name; //文件系统的称名 rt_uint32_t flags; //操纵标记 /* mount and unmount file system */ int (*mount) (struct dfs_filesystem *fs, unsigned long rwflag, const void *data); //挂载 int (*unmount) (struct dfs_filesystem *fs); //消取挂载 /* make a file system */ int (*mkfs) (rt_device_t devid); //创立一个文件系统操纵,相当于格式化 int (*statfs) (struct dfs_filesystem *fs, struct statfs *buf); //获得文件系统的以后态状信息操纵 int (*open) (struct dfs_fd *fd); //打开文件操纵 int (*close) (struct dfs_fd *fd); //关闭文件操纵 int (*ioctl) (struct dfs_fd *fd, int cmd, void *args); //文件控制操纵 int (*read) (struct dfs_fd *fd, void *buf, rt_size_t count); //文件读取操纵 int (*write) (struct dfs_fd *fd, const void *buf, rt_size_t count); //文件写操纵 int (*flush) (struct dfs_fd *fd); //将文件容内保存到设备上操纵 int (*lseek) (struct dfs_fd *fd, rt_off_t offset); //文件容内位定操纵 int (*getdents) (struct dfs_fd *fd, struct dirent *dirp, rt_uint32_t count); //获得目录条目操纵 int (*unlink) (struct dfs_filesystem *fs, const char *pathname); //将一目录从文件系统移除操纵 int (*stat) (struct dfs_filesystem *fs, const char *filename, struct stat *buf); //获得文件态状信息 int (*rename) (struct dfs_filesystem *fs, const char *oldpath, const char *newpath); //文件重定名操纵 };
由面上的文件操纵义定可知,filesystem_opration_table内个一每表项保存的是一个文件系统的一套操纵数函,不管是什么文件系统,其操纵数函的式形都是致一的。
2.2.2 filesystem_table
filesystem_table录记的是以后挂载的文件系统,其个一每表项表现的就是一个文件系统。
上面再来看看文件系统的义定.
/* Mounted file system */ struct dfs_filesystem { rt_device_t dev_id; //此文件系统对应的设备ID char *path; //此文件系统的挂载点 const struct dfs_filesystem_operation *ops; //此文件系统对应的操纵接口集,指向filesystem_operation_table对应的表项.其义定见2.2.1。 void *data; //文件系统的私有数据 };
从面上可以看出,文件系统就是将文件系统操纵的集合,对应的设备ID以及挂载点整合在一起的数据集合。
2.2.3 fd_table
fd_table录记以后打开的文件集合,个一每表项表现一个打开的文件句柄,其结构如下义定:
/* file descriptor */ #define DFS_FD_MAGIC 0xfdfd struct dfs_fd { rt_uint16_t magic; //文件述描魔数 rt_uint16_t type; //文件类型 char *path; //相对挂载点的路径 int ref_count; //以后被联关的次数 struct dfs_filesystem *fs; //对应的文件系统 rt_uint32_t flags; //标记 rt_size_t size; //文件大小 rt_off_t pos; //以后文件位置 void *data; //私有数据 };
2.3.4 dfs小结
由以上几节容内可知,dfs架框要重由三张表成组,而dfs.c,dfs_fs.c,dfs_file.c这三个源文件要重是环绕这三张表来转的,供给一些在关这三张表的添加,删除,找查,获得等操纵,而真正的文件操纵是不并在dfs架框内实现,而是由中间层的体具文件系统来实现,中间层的文件系统,比如elmfat文件系统通过向filesystem_operation_table册注其操纵集,向filesystem_table挂载其文件系统,这样一来,系统就够能通过这两张表找到对应的体具操纵数函了。
3 RTT文件系统初始化进程
RTT在在关文件系统的初始化进程中一般按以下流程来行进:
1 dfs架框初始化(最顶层初始化)
2 中间层体具文件系统初始化(中间层体具文件系统初始化)
3 文件系统对应的体具设备驱动初始化(最底层设备驱动初始化)
4 挂载文件系统(将各层体具联关起来)
3.1 dfs架框初始化(最顶层)
其源码如下:
void dfs_init(void) { rt_memset((void *)filesystem_operation_table, 0, sizeof(filesystem_operation_table));//清空filesystem_operation_table表 rt_memset(filesystem_table, 0, sizeof(filesystem_table));//清空filesystem_table表 rt_memset(fd_table, 0, sizeof(fd_table));//清空 /* create device filesystem lock */ rt_mutex_init(&fslock, "fslock", RT_IPC_FLAG_FIFO);//文件系统互斥锁初始化 #ifdef DFS_USING_WORKDIR /* set current working directory */ rt_memset(working_directory, 0, sizeof(working_directory));//作工路径初始化 working_directory[0] = '/'; #endif }
由上源码可知,dfs架框初始化只是对部内表及资源行进初始化。
3.2 中间层体具文件系统初始化(中间层)
以RTT的elmfat文件系统为例,此骤步要重是向DFS架框册注elmfat文件系统的操纵数函集,即向filesystem_operation_table册注elmfat文件系统的关相操纵。
int elm_init(void)//elmfat文件系统初始化 { /* register fatfs file system */ dfs_register(&dfs_elm);//册注elmfat文件系统 return 0; } int dfs_register(const struct dfs_filesystem_operation *ops) { int index, result; int free_index; result = 0; free_index = DFS_FILESYSTEM_TYPES_MAX; //首先获得文件操纵限权 dfs_lock(); //检查该文件系统否是已册注 for (index = 0; index < DFS_FILESYSTEM_TYPES_MAX; index++) { if (filesystem_operation_table[index] == RT_NULL) { /* find out an empty filesystem type entry */ if (free_index == DFS_FILESYSTEM_TYPES_MAX)//录记第一个闲暇位置 free_index = index; } else if (strcmp(filesystem_operation_table[index]->name, ops->name) == 0)//如果已册注过了,则回返错误 { result = -1; goto err; } } /* filesystem type table full */ if (free_index == DFS_FILESYSTEM_TYPES_MAX)//如果全体已满,则回返错误 { result = -1; goto err; } /* save the filesystem's operations */ filesystem_operation_table[free_index] = ops;//将以后操纵集合录记到闲暇位置 err: dfs_unlock();//释放文件系统操纵限权 return result; }
由上述码代可知,面上的操纵只是当体具的文件系统册注进filesystem_operation_table中,还没体具的操纵。
3.3 文件系统对应的体具设备初始化(底层驱动)
接下来就是要对文件系统体具的设备驱动行进初始化,比如spi flash,sd等,这部分容内在这里不做概况分析,后续将分析。这里只要晓得经过这么一初始化后,文件系统对应的体具设备就够能操纵了。
3.4 挂载文件系统
挂载文件系统在文件系统初始化骤步中是最后的一步,也是最要重的一步。挂载功成后用户就够能通过dfs_posix.c文件供给的文件接口数函对文件系统行进意任操纵了。
上面来看看这个文件挂载到底做了哪些事件?
/* mount SPI flash as root directory */ if (dfs_mount("flash0", "/", "elm", 0, 0) == 0)//挂载名字为elm的文件系统,这个文件系统对应的设备名为flash0,挂载点为/ { rt_kprintf("flash0 mount to /.\n"); } else { rt_kprintf("flash0 mount to / failed.\n"); }
由上可知,明说挂载的进程可以看成是将某某名的文件系统和某某名的设备驱动关相起来,并挂载到某个挂载点,这个挂载点即为这个文件系统的根目录。
dfs_mount数函如下义定:
int dfs_mount(const char *device_name, const char *path, const char *filesystemtype, unsigned long rwflag, const void *data) { const struct dfs_filesystem_operation *ops; struct dfs_filesystem *fs; char *fullpath=RT_NULL; rt_device_t dev_id; int index, free_index; //找查对应的设备ID /* open specific device */ if (device_name != RT_NULL) { dev_id = rt_device_find(device_name); if (dev_id == RT_NULL) { /* no this device */ rt_set_errno(-DFS_STATUS_ENODEV); return -1; } } else { /* which is a non-device filesystem mount */ dev_id = RT_NULL; } /* find out specific filesystem */ dfs_lock(); //在filesystem_operation_table中想找对应的文件系统 for (index = 0; index < DFS_FILESYSTEM_TYPES_MAX; index++) { if (filesystem_operation_table[index] == RT_NULL) continue; if (strcmp(filesystem_operation_table[index]->name, filesystemtype) == 0) break; } dfs_unlock(); /* can't find filesystem */ if (index == DFS_FILESYSTEM_TYPES_MAX)//如果没有找到,则明说以后未册注此文件系统 { rt_set_errno(-DFS_STATUS_ENODEV); return -1; } //ops录记应相的文件系统 ops = filesystem_operation_table[index]; /* make full path for special file */ //标准化路径 fullpath = dfs_normalize_path(RT_NULL, path); if (fullpath == RT_NULL) /* not an abstract path */ { rt_set_errno(-DFS_STATUS_ENOTDIR); return -1; } /* Check if the path exists or not, raw APIs call, fixme */ //如果该目录不为/或/dev,则检查该路径否是存在,如果不存在,则回返错误 if ((strcmp(fullpath, "/") != 0) && (strcmp(fullpath, "/dev") != 0)) { struct dfs_fd fd; if (dfs_file_open(&fd, fullpath, DFS_O_RDONLY | DFS_O_DIRECTORY) < 0) { rt_free(fullpath); rt_set_errno(-DFS_STATUS_ENOTDIR); return -1; } dfs_file_close(&fd); } //在挂载文件系统中找查该文件系统否是已挂载过 free_index = DFS_FILESYSTEMS_MAX; /* check whether the file system mounted or not */ dfs_lock(); for (index = 0; index < DFS_FILESYSTEMS_MAX; index ++) { if (filesystem_table[index].ops == RT_NULL) { /* find out an empty filesystem table entry */ if (free_index == DFS_FILESYSTEMS_MAX) free_index = index;//录记第一个闲暇位置 } else if (strcmp(filesystem_table[index].path, path) == 0)//ÒѾ¹ÒÔØ { rt_set_errno(-DFS_STATUS_EINVAL); goto err1; } } /* can't find en empty filesystem table entry */ if (free_index == DFS_FILESYSTEMS_MAX)//已满,则回返错误 { rt_set_errno(-DFS_STATUS_ENOSPC); goto err1; } /* register file system */ //册注该文件系统 fs = &(filesystem_table[free_index]);//文件系统 fs->path = fullpath;//挂载点 fs->ops = ops;//操纵集合 fs->dev_id = dev_id;//设备ID /* release filesystem_table lock */ dfs_unlock();//关相的文件互斥锁 /* open device, but do not check the status of device */ if (dev_id != RT_NULL) rt_device_open(fs->dev_id, RT_DEVICE_OFLAG_RDWR); //以读写方法打开该设备 //行执挂载操纵 /* there is no mount implementation */ if (ops->mount == RT_NULL)//如果挂载数函为空,则回返错误 { if (dev_id != RT_NULL) rt_device_close(dev_id); dfs_lock(); /* clear filesystem table entry */ rt_memset(fs, 0, sizeof(struct dfs_filesystem)); dfs_unlock(); rt_free(fullpath); rt_set_errno(-DFS_STATUS_ENOSYS); return -1; } /* call mount of this filesystem */ else if (ops->mount(fs, rwflag, data) < 0)//如果挂载操纵失败,则回返错误 { /* close device */ if (dev_id != RT_NULL) rt_device_close(fs->dev_id); /* mount failed */ dfs_lock(); /* clear filesystem table entry */ rt_memset(fs, 0, sizeof(struct dfs_filesystem)); dfs_unlock(); rt_free(fullpath); return -1; } return 0; err1: dfs_unlock(); if (fullpath != RT_NULL) rt_free(fullpath); return -1; }
由上可知,挂载操纵实际上是首先在filesystem_oeration_table中找查该文件系统否是已册注过,如未册注则回返错误,接下来在filesystem_table中检查该文件系统否是已挂载,如果已挂载过,则回返错误。接着打开对应设备,最后行执该文件系统操纵会合供给的mount数函。
只有挂载功成后的文件系统,RTT才能通过其供给的一系统操纵接口来对对应设备行进文件操纵了。
4 dfs架框总结
由以上容内可知,dfs架框并未供给体具的文件操纵实现,而只是供给了一个架框,可以让林林总总的文件系统适配到这个架框上来,而坚持顶层的dfs_posix接口稳定,对于用户说来,只要晓得posix文件接口就够能了,而不必心关部内实现细节。文件操纵的真正实现细节是在各种文件系统中,RTT的elmfat就是其中一种,其关相操纵由filesystem_operation_table来理管。
时暂写到这,接下来将会论讨下RTT的elmfat文件系统。
文章结束给大家分享下程序员的一些笑话语录:
看到有人回帖“不顶不是中国人”,他的本意是想让帖子沉了。