Zephyr文件系统

Creative Commons
本作品采用知识共享署名

本文说明Zephyr虚拟文件系统的架构,原理,和驱动的关联操作。不涉及具体文件系统的实现方式。

简介

Zephyr提供的文件系统功能允许在不同的挂载点挂载多个文件系统,每个挂载点都维护有关实例化,mount,操作当前文件系统的必要信息。通过文件系统注册机制,将应用和直接访问文件系统特定API解耦合。
Zephyr的文件系统目前支援fatfs和nffs,且这两种fs都最后都访问的是flash,fatfs还可以访问ram和sd卡。暂时还没看到对真实disk的支援。本文省略了ram和sd卡的情况,以flash为例进行说明。

架构

下图说明了Zephyr文件系统的架构
fs

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
2
3
4
5
6
static int fs_init(struct device *dev)
{
k_mutex_init(&mutex);
sys_dlist_init(&fs_mnt_list);
return 0;
}

文件系统注册

VFS提供两个API用于注册VFS实现层提供的操作函数struct fs_file_system_t函数如下

1
2
int fs_register(enum fs_type type, struct fs_file_system_t *fs);
int fs_unregister(enum fs_type type, struct fs_file_system_t *fs);

前zephyr VFS只支援fatfs和nffs两种类型

1
2
3
4
5
enum fs_type {
FS_FATFS = 0,
FS_NFFS,
FS_TYPE_END,
};

不同的文件系统类型对用不同的操作函数,为了达到解耦合的目的,都被抽象为统一的函数结构struct fs_file_system_t

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
struct fs_file_system_t {
/* File operations */
int (*open)(struct fs_file_t *filp, const char *fs_path);
ssize_t (*read)(struct fs_file_t *filp, void *dest, size_t nbytes);
ssize_t (*write)(struct fs_file_t *filp,
const void *src, size_t nbytes);
int (*lseek)(struct fs_file_t *filp, off_t off, int whence);
off_t (*tell)(struct fs_file_t *filp);
int (*truncate)(struct fs_file_t *filp, off_t length);
int (*sync)(struct fs_file_t *filp);
int (*close)(struct fs_file_t *filp);
/* Directory operations */
int (*opendir)(struct fs_dir_t *dirp, const char *fs_path);
int (*readdir)(struct fs_dir_t *dirp, struct fs_dirent *entry);
int (*closedir)(struct fs_dir_t *dirp);
/* File system level operations */
int (*mount)(struct fs_mount_t *mountp);
int (*unmount)(struct fs_mount_t *mountp);
int (*unlink)(struct fs_mount_t *mountp, const char *name);
int (*rename)(struct fs_mount_t *mountp, const char *from,
const char *to);
int (*mkdir)(struct fs_mount_t *mountp, const char *name);
int (*stat)(struct fs_mount_t *mountp, const char *path,
struct fs_dirent *entry);
int (*statvfs)(struct fs_mount_t *mountp, const char *path,
struct fs_statvfs *stat);
};

文件系统挂载

文件系统挂载函数,将指定的device以指定的文件系统挂载到指定的路径下,这里简化一下实现代码,看一下主要是做了什么事情

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
int fs_mount(struct fs_mount_t *mp)
{
//根据指定的文件系统类型取得操作函数
fs = fs_map[mp->type];

//在mount点链表中检查mount点十分已存在
mp->mountp_len = strlen(mp->mnt_point);
SYS_DLIST_FOR_EACH_NODE(&fs_mnt_list, node) {
itr = CONTAINER_OF(node, struct fs_mount_t, node);
/* continue if length does not match */
if (mp->mountp_len != itr->mountp_len) {
continue;
}

if (strncmp(mp->mnt_point, itr->mnt_point,
mp->mountp_len) == 0) {
LOG_ERR("mount Point already exists!!");
rc = -EBUSY;
goto mount_err;
}
}

//执行实际的mount
rc = fs->mount(mp);

//设置mount点操作函数
mp->fs = fs;

//将新的mount点加入到mount点链表中
sys_dlist_append(&fs_mnt_list, &mp->node);
}
int fs_unmount(struct fs_mount_t *mp)
{
//执行实际的unmount
rc = mp->fs->unmount(mp);

//清除mount点操作函数
mp->fs = NULL;

//将mount点从mount点list中移除
sys_dlist_remove(&mp->node);
}

从上面分析可以看到mount点管理的mount信息并不会再存一份,而是直接将mp加入链表,因此fs_mount函数的使用者要保存struct fs_mount_t。fs_mount的使用者必须要传入device,mount路径和mount的文件类型

1
2
3
4
5
6
7
8
9
struct fs_mount_t {
sys_dnode_t node;
enum fs_type type; //必要输入,mount的文件系统类型
const char *mnt_point; //必要输入mount点
void *fs_data;
void *storage_dev; //必要输入device
size_t mountp_len;
const struct fs_file_system_t *fs;
};

文件操作

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//使用nffs_fs VFS实现层API初始化fs_file_system_t
static struct fs_file_system_t nffs_fs = {
.open = nffs_open,
.close = nffs_close,
.read = nffs_read,
.write = nffs_write,
.lseek = nffs_seek,
.tell = nffs_tell,
.truncate = nffs_truncate,
.sync = nffs_sync,
.opendir = nffs_opendir,
.readdir = nffs_readdir,
.closedir = nffs_closedir,
.mount = nffs_mount,
.unlink = nffs_unlink,
.rename = nffs_rename,
.mkdir = nffs_mkdir,
.stat = nffs_stat,
.statvfs = nffs_statvfs,
};

//在nffs_init初始化时将nffs_fs的fs_file_system_t注册到VFS接口层内
static int nffs_init(struct device *dev)
{
ARG_UNUSED(dev);

k_mutex_init(&nffs_lock);

return fs_register(FS_NFFS, &nffs_fs);
}

//nffs_init该函数被注册为系统函数,无需再app中使用,在系统初始化时被调用
SYS_INIT(nffs_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int nffs_os_flash_write(uint8_t id, uint32_t address, const void *src,
uint32_t num_bytes)
{
int rc;

rc = flash_write_protection_set(flash_dev, false);
if (rc) {
return rc;
}

rc = flash_write(flash_dev, address, src, num_bytes);

/* Ignore errors here - this does not affect write operation */
(void) flash_write_protection_set(flash_dev, true);

return rc;
}

因此对于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
2
3
4
5
6
7
8
9
10
11
12
13
static int disk_flash_access_init(struct disk_info *disk)
{
if (flash_dev) {
return 0;
}
//使用CONFIG_DISK_FLASH_DEV_NAME做为device
flash_dev = device_get_binding(CONFIG_DISK_FLASH_DEV_NAME);
if (!flash_dev) {
return -ENODEV;
}

return 0;
}

然后通过这个device进行写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
static int update_flash_block(off_t start_addr, u32_t size, const void *buff)
{
off_t fl_addr;
u8_t *src = (u8_t *)buff;
u32_t num_write;

/* if size is a partial block, perform read-copy with user data */
if (size < CONFIG_DISK_ERASE_BLOCK_SIZE) {
int rc;

rc = read_copy_flash_block(start_addr, size, buff, fs_buff);
if (rc != 0) {
return -EIO;
}

/* now use the local buffer as the source */
src = (u8_t *)fs_buff;
}

/* always align starting address for flash write operation */
fl_addr = ROUND_DOWN(start_addr, CONFIG_DISK_FLASH_ERASE_ALIGNMENT);

/* disable write-protection first before erase */
flash_write_protection_set(flash_dev, false);
if (flash_erase(flash_dev, fl_addr, CONFIG_DISK_ERASE_BLOCK_SIZE)
!= 0) {
return -EIO;
}

/* write data to flash */
num_write = GET_NUM_BLOCK(CONFIG_DISK_ERASE_BLOCK_SIZE,
CONFIG_DISK_FLASH_MAX_RW_SIZE);

for (u32_t i = 0; i < num_write; i++) {
/* flash_write reenabled write-protection so disable it again */
flash_write_protection_set(flash_dev, false);

if (flash_write(flash_dev, fl_addr, src,
CONFIG_DISK_FLASH_MAX_RW_SIZE) != 0) {
return -EIO;
}

fl_addr += CONFIG_DISK_FLASH_MAX_RW_SIZE;
src += CONFIG_DISK_FLASH_MAX_RW_SIZE;
}

return 0;
}

参考

https://docs.zephyrproject.org/latest/reference/file_system/index.html