本文说明Zephyr虚拟文件系统的架构,原理,和驱动的关联操作。不涉及具体文件系统的实现方式。
简介
Zephyr提供的文件系统功能允许在不同的挂载点挂载多个文件系统,每个挂载点都维护有关实例化,mount,操作当前文件系统的必要信息。通过文件系统注册机制,将应用和直接访问文件系统特定API解耦合。
Zephyr的文件系统目前支援fatfs和nffs,且这两种fs都最后都访问的是flash,fatfs还可以访问ram和sd卡。暂时还没看到对真实disk的支援。本文省略了ram和sd卡的情况,以flash为例进行说明。
架构
下图说明了Zephyr文件系统的架构
VFS
VFS可以看做由两层组成:
- VFS接口层:fs.c 对上提供FS统一接口,对下呼叫fs 操作函数,达到解耦合的目的。在其内部对挂载点进行管理
- VFS实现层:使用fs的实际API实现统一的fs callback,提供VFS功能。
VFS接口层
subsys/fs/fs.c
初始化
初始化使用fs_init,该函数被注册为系统函数,无需再app中使用,在系统初始化时被调用
1 | SYS_INIT(fs_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); |
fs_init完成对挂载点链表的初始化
1 | static int fs_init(struct device *dev) |
文件系统注册
VFS提供两个API用于注册VFS实现层提供的操作函数struct fs_file_system_t函数如下
1 | int fs_register(enum fs_type type, struct fs_file_system_t *fs); |
前zephyr VFS只支援fatfs和nffs两种类型
1 | enum fs_type { |
不同的文件系统类型对用不同的操作函数,为了达到解耦合的目的,都被抽象为统一的函数结构struct fs_file_system_t
1 | struct fs_file_system_t { |
文件系统挂载
文件系统挂载函数,将指定的device以指定的文件系统挂载到指定的路径下,这里简化一下实现代码,看一下主要是做了什么事情
1 | int fs_mount(struct fs_mount_t *mp) |
从上面分析可以看到mount点管理的mount信息并不会再存一份,而是直接将mp加入链表,因此fs_mount函数的使用者要保存struct fs_mount_t。fs_mount的使用者必须要传入device,mount路径和mount的文件类型
1 | struct fs_mount_t { |
文件操作
vfs的文件操作函数基本是直接呼叫注册进来的VFS实现层struct fs_file_system_t,这里不用多做分析,罗列一下函数,具体使用可以参考官方文档
- 文件
int fs_open(struct fs_file_t zfp, const char file_name)
int fs_close(struct fs_file_t zfp)
ssize_t fs_read(struct fs_file_t zfp, void ptr, size_t size)
ssize_t fs_write(struct fs_file_t zfp, const void ptr, size_t size)
int fs_seek(struct fs_file_t zfp, off_t offset, int whence)
off_t fs_tell(struct fs_file_t zfp)
int fs_truncate(struct fs_file_t zfp, off_t length)
int fs_sync(struct fs_file_t zfp)
文件夹
int fs_opendir(struct fs_dir_t zdp, const char abs_path)
int fs_readdir(struct fs_dir_t zdp, struct fs_dirent entry)
int fs_closedir(struct fs_dir_t zdp)
文件系统
int fs_mkdir(const char abs_path)
int fs_unlink(const char abs_path)
int fs_rename(const char from, const char to)
int fs_stat(const char abs_path, struct fs_dirent entry)
int fs_statvfs(const char abs_path, struct fs_statvfs stat)
VFS实现层
subsys/fs/fat_fs.c, subsys/fs/nffs_fs.c
VFS实现层本身并不涉及文件系统的实现,而是通过调用组织实际文件系统函数的实现文件系统的统一接口,并注册到VFS接口层中。本文无意说明FS如何实现,因此只看一下如何注册。
以nffs_fs.c为例:
1 | //使用nffs_fs VFS实现层API初始化fs_file_system_t |
VFS如何使用Flash驱动
目前zephyr实现了fatfs和nffs,代码放在ext/fs下,本文无意说明FS如何实现,这里以文件的write为例说明如何call到驱动层
nffs
nffs_write->nffs_write_to_file->nffs_write_chunk->nffs_write_over_block->nffs_flash_write->nffs_os_flash_write->flash_write
nffs实现时通过调用nffs VFS实现层的函数nffs_os_flash_write来访问flash驱动。这里看一下这里flash驱动如何知道要操作哪一个device呢:
在vfs实现层的nffs_mount时会将mount的device保存在vfs实现层内
1 | flash_dev = (struct device *)mountp->storage_dev; |
在flash_write的时候就是使用的这个device
1 | int nffs_os_flash_write(uint8_t id, uint32_t address, const void *src, |
因此对于nffs来说,fs使用的device就是mount的时候指定的storage_dev
fatfs
fatfs_write->f_write->disk_write->disk_access_write->disk->ops->write(disk_flash_access_write)->update_flash_block->flash_write
fatfs通过disk_access对flash进行访问,disk_access要访问flash,最终是通过disk_flash_access_write,具体访问哪个device,不是由mount时指定的storage_dev,而是disk_flash_access_init时自己指定的
1 | static int disk_flash_access_init(struct disk_info *disk) |
然后通过这个device进行写
1 | static int update_flash_block(off_t start_addr, u32_t size, const void *buff) |
参考
https://docs.zephyrproject.org/latest/reference/file_system/index.html