0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

鸿蒙轻内核源码分析:虚拟文件系统 VFS

王程 来源: jf_75796907 作者: jf_75796907 2024-02-18 14:50 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

VFS(Virtual File System)是文件系统的虚拟层,它不是一个实际的文件系统,而是一个异构文件系统之上的软件粘合层,为用户提供统一的类 Unix 文件操作接口。由于不同类型的文件系统接口不统一,若系统中有多个文件系统类型,访问不同的文件系统就需要使用不同的非标准接口。而通过在系统中添加 VFS 层,提供统一的抽象接口,屏蔽了底层异构类型的文件系统的差异,使得访问文件系统的系统调用不用关心底层的存储介质和文件系统类型,提高开发效率。本文先介绍下 VFS 的结构体和全局变量,然后详细分析下 VFS 文件操作接口。文中所涉及的源码,均可以在开源站点 https://gitee.com/openharmony/kernel_liteos_m 获取。

1、VFS 结构体定义

在文件 componentsfsvfsfs_operations.h 中定义了 VFS 虚拟文件系统操作涉及的结构体。⑴处的 struct MountOps 结构体封装了挂载相关的操作,包含挂载、卸载和文件系统统计操作。⑵处的 struct FsMap 结构体映射文件系统类型及其对应的挂载操作和文件系统操作,支持的文件类型包含 “fat” 和 “littlefs” 两种,通过这个结构体可以获取对应文件类型的挂载操作及文件系统操作接口。⑶处的 struct FileOps 封装文件系统的操作接口,包含文件操作、目录操作,统计等相应的接口。

⑴  struct MountOps {
        int (*Mount)(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags,
            const void *data);
        int (*Umount)(const char* target);
        int (*Umount2)(const char* target, int flag);
        int (*Statfs)(const char *path, struct statfs *buf);
    };

⑵  struct FsMap {
        const char *fileSystemtype;
        const struct MountOps *fsMops;
        const struct FileOps *fsFops;
    };

⑶  struct FileOps {
        int (*Open)(const char *path, int openFlag, ...);
        int (*Close)(int fd);
        int (*Unlink)(const char *fileName);
        int (*Rmdir)(const char *dirName);
        int (*Mkdir)(const char *dirName, mode_t mode);
        struct dirent *(*Readdir)(DIR *dir);
        DIR *(*Opendir)(const char *dirName);
        int (*Closedir)(DIR *dir);
        int (*Read)(int fd, void *buf, size_t len);
        int (*Write)(int fd, const void *buf, size_t len);
        off_t (*Seek)(int fd, off_t offset, int whence);
        int (*Getattr)(const char *path, struct stat *buf);
        int (*Rename)(const char *oldName, const char *newName);
        int (*Fsync)(int fd);
        int (*Fstat)(int fd, struct stat *buf);
        int (*Stat)(const char *path, struct stat *buf);
        int (*Ftruncate)(int fd, off_t length);
    };

2、VFS 重要的内部全局变量

在文件 componentsfsvfslos_fs.c 中有 2 个全局变量比较重要,⑴处定义的数组 g_fsmap 维护文件系统类型映射信息,数组大小为 2,支持 “fat” 和 “littlefs” 文件类型。⑵处的变量 g_fs 根据挂载的文件类型指向数组 g_fsmap 中的 FsMap 类型元素。⑶处的函数 InitMountInfo () 会给数组 g_fsmap 进行初始化赋值。第 0 个元素维护的 “fat” 文件类型的文件系统映射信息,第 1 个元素维护的 “littlefs” 文件类型的文件系统映射信息。涉及到的挂载操作、文件系统操作变量 g_fatfsMnt、g_fatfsFops、g_lfsMnt、g_lfsFops 在对应的文件系统文件中定义。⑷处的函数 MountFindfs () 用于根据文件类型从数组中获取文件映射信息。

⑴  static struct FsMap g_fsmap[MAX_FILESYSTEM_LEN] = {0};
⑵  static struct FsMap *g_fs = NULL;

⑶  static void InitMountInfo(void)
    {
    #if (LOSCFG_SUPPORT_FATFS == 1)
        extern struct MountOps g_fatfsMnt;
        extern struct FileOps g_fatfsFops;
        g_fsmap[0].fileSystemtype = strdup("fat");
        g_fsmap[0].fsMops = &g_fatfsMnt;
        g_fsmap[0].fsFops = &g_fatfsFops;
    #endif
    #if (LOSCFG_SUPPORT_LITTLEFS == 1)
        extern struct MountOps g_lfsMnt;
        extern struct FileOps g_lfsFops;
        g_fsmap[1].fileSystemtype = strdup("littlefs");
        g_fsmap[1].fsMops = &g_lfsMnt;
        g_fsmap[1].fsFops = &g_lfsFops;
    #endif
    }

⑷  static struct FsMap *MountFindfs(const char *fileSystemtype)
    {
        struct FsMap *m = NULL;

        for (int i = 0; i < MAX_FILESYSTEM_LEN; i++) {
            m = &(g_fsmap[i]);
            if (m- >fileSystemtype && strcmp(fileSystemtype, m->fileSystemtype) == 0) {
                return m;
            }
        }

        return NULL;
    }

3、VFS 相关的操作接口

在之前的系列文章《鸿蒙轻内核 M 核源码分析系列十九 Musl LibC》中介绍了相关的接口,那些接口会调用 VFS 文件系统中操作接口。对每个接口的用途用法不再描述,快速记录下各个操作接口。

3.1 挂载卸载操作​

挂载卸载操作包含 LOS_FsMount、LOS_FsUmount、LOS_FsUmount2 等 3 个操作。⑴处在挂载文件系统之前,需要初始化文件系统映射信息,只会操作一次。⑵处根据文件系统类型获取对应的文件类型映射信息。从这里,可以获知,LiteOS-M 内核只能同时支持一个文件系统,不能只支持 fat 又支持 littlefs。⑶处对应对应的文件系统挂载接口实现挂载操作。其他两个函数同样比较简单,自行阅读代码即可。

int LOS_FsMount(const char *source, const char *target,
                    const char *filesystemtype, unsigned long mountflags,
                    const void *data)
    {
        static int initFlag = 0;

⑴      if (initFlag == 0) {
            InitMountInfo();
            initFlag = 1;
        }

⑵      g_fs = MountFindfs(filesystemtype);
        if (g_fs == NULL) {
            errno = ENODEV;
            return FS_FAILURE;
        }

        if (g_fs->fsMops == NULL || g_fs->fsMops->Mount == NULL) {
            errno = ENOSYS;
            return FS_FAILURE;
        }

⑶      return g_fs->fsMops->Mount(source, target, filesystemtype, mountflags, data);
    }

    int LOS_FsUmount(const char *target)
    {
        if (g_fs == NULL) {
            errno = ENODEV;
            return FS_FAILURE;
        }
        if (g_fs->fsMops == NULL || g_fs->fsMops->Umount == NULL) {
            errno = ENOSYS;
            return FS_FAILURE;
        }
        return g_fs->fsMops->Umount(target);
    }

    int LOS_FsUmount2(const char *target, int flag)
    {
        if (g_fs == NULL) {
            errno = ENODEV;
            return FS_FAILURE;
        }
        if (g_fs->fsMops == NULL || g_fs->fsMops->Umount2 == NULL) {
            errno = ENOSYS;
            return FS_FAILURE;
        }
        return g_fs->fsMops->Umount2(target, flag);
    }

3.2 文件目录操作​

VFS 封装的文件目录操作接口包含 LOS_Open、LOS_Close、LOS_Read、LOS_Write、LOS_Opendir、LOS_Readdir、LOS_Closedir 等等。对具体的文件类型的文件目录操作接口进行封装,代码比较简单,自行阅读即可,部分代码片段如下。

......

int LOS_Unlink(const char *path)
{
    if (g_fs == NULL) {
        errno = ENODEV;
        return FS_FAILURE;
    }
    if (g_fs->fsFops == NULL || g_fs->fsFops->Unlink == NULL) {
        errno = ENOSYS;
        return FS_FAILURE;
    }
    return g_fs->fsFops->Unlink(path);
}

int LOS_Fstat(int fd, struct stat *buf)
{
    if (g_fs == NULL) {
        errno = ENODEV;
        return FS_FAILURE;
    }
    if (g_fs->fsFops == NULL || g_fs->fsFops->Fstat == NULL) {
        errno = ENOSYS;
        return FS_FAILURE;
    }
    return g_fs->fsFops->Fstat(fd, buf);
}

......

int LOS_Mkdir(const char *path, mode_t mode)
{
    if (g_fs == NULL) {
        errno = ENODEV;
        return FS_FAILURE;
    }
    if (g_fs->fsFops == NULL || g_fs->fsFops->Mkdir == NULL) {
        errno = ENOSYS;
        return FS_FAILURE;
    }
    return g_fs->fsFops->Mkdir(path, mode);
}

DIR *LOS_Opendir(const char *dirName)
{
    if (g_fs == NULL) {
        errno = ENODEV;
        return NULL;
    }
    if (g_fs->fsFops == NULL || g_fs->fsFops->Opendir == NULL) {
        errno = ENOSYS;
        return NULL;
    }
    return g_fs->fsFops->Opendir(dirName);
}
......

3.3 随机数文件​

文件 /dev/random 可以用于产生随机数。在开启宏 LOSCFG_RANDOM_DEV 时,LiteOS-M 支持随机数文件。从⑴处可知随机数依赖文件~/openharmony/base/security/huks/interfaces/innerkits/huks_lite/hks_client.h 和 hks_tmp_client.c,这些文件用来产生随机数。⑵处定义的 RANDOM_DEV_FD 和 RANDOM_DEV_PATH 分别是随机数文件的文件描述符和随机数文件路径。

#ifdef LOSCFG_RANDOM_DEV
⑴  #include "hks_client.h"
⑵  #define RANDOM_DEV_FD  CONFIG_NFILE_DESCRIPTORS + CONFIG_NSOCKET_DESCRIPTORS
    #define RANDOM_DEV_PATH  "/dev/random"
    #endif

3.3.1 随机 LOS_Open 和 LOS_Close

该函数打开一个文件,获取文件描述符用于进一步操作。⑴处表示对于随机数文件,打开的标签选项只能支持指定的这些,否则会返回错误码。⑵处获取标准路径,如果获取失败,返回错误码。⑶处比较获取的标准路径是否为 RANDOM_DEV_PATH,在确认是随机数路径时,⑷处开始判断。如果访问模式为只读,返回错误,如果打开选项标签是目录,返回错误。如果不是上述错误情形,返回随机数文件描述符。⑸处如果获取的标准路径为 “/” 或 “/dev”,则根据不同的选项,返回不同的错误码。

int LOS_Open(const char *path, int oflag, ...)
{
#ifdef LOSCFG_RANDOM_DEV
    unsigned flags = O_RDONLY | O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_LARGEFILE | O_TRUNC | O_EXCL | O_DIRECTORY;
⑴  if ((unsigned)oflag & ~flags) {
        errno = EINVAL;
        return FS_FAILURE;
    }

    size_t pathLen = strlen(path) + 1;
    char *canonicalPath = (char *)malloc(pathLen);
    if (!canonicalPath) {
        errno = ENOMEM;
        return FS_FAILURE;
    }
⑵  if (GetCanonicalPath(NULL, path, canonicalPath, pathLen) == 0) {
        FREE_AND_SET_NULL(canonicalPath);
        errno = ENOMEM;
        return FS_FAILURE;
    }

⑶  if (strcmp(canonicalPath, RANDOM_DEV_PATH) == 0) {
        FREE_AND_SET_NULL(canonicalPath);
⑷      if ((O_ACCMODE & (unsigned)oflag) != O_RDONLY) {
            errno = EPERM;
            return FS_FAILURE;
        }
        if ((unsigned)oflag & O_DIRECTORY) {
            errno = ENOTDIR;
            return FS_FAILURE;
        }
        return RANDOM_DEV_FD;
    }
⑸  if (strcmp(canonicalPath, "/") == 0 || strcmp(canonicalPath, "/dev") == 0) {
        FREE_AND_SET_NULL(canonicalPath);
        if ((unsigned)oflag & O_DIRECTORY) {
            errno = EPERM;
            return FS_FAILURE;
        }
        errno = EISDIR;
        return FS_FAILURE;
    }
    FREE_AND_SET_NULL(canonicalPath);
#endif
......
}

对于随机数文件,关闭时,直接返回成功,不需要额外操作。代码片段如下:

int LOS_Close(int fd)
{
#ifdef LOSCFG_RANDOM_DEV
    if (fd == RANDOM_DEV_FD) {
        return FS_SUCCESS;
    }
#endif
......
}

3.3.2 随机 LOS_Read 和 LOS_Write

随机数文件读写使用 LOS_Read 和 LOS_Write 接口。读取时,⑴处先对传入参数进行校验,如果读取字节数为 0,则返回 0;如果读取的缓存地址为空,返回 - 1;如果读的字节大于 1024,则使用 1024。⑵处调用 hks_generate_random () 产生随机数。由于随机数文件是只读的,如果尝试写入会返回 - 1 错误码。

ssize_t LOS_Read(int fd, void *buf, size_t nbyte)
{
#ifdef LOSCFG_RANDOM_DEV
    if (fd == RANDOM_DEV_FD) {
⑴      if (nbyte == 0) {
            return FS_SUCCESS;
        }
        if (buf == NULL) {
            errno = EINVAL;
            return FS_FAILURE;
        }
        if (nbyte > 1024) { /* 1024, max random_size */
            nbyte = 1024; /* hks_generate_random: random_size must <= 1024 */
        }
        struct hks_blob key = {HKS_BLOB_TYPE_RAW, (uint8_t *)buf, nbyte};
⑵      if (hks_generate_random(&key) != 0) {
            errno = EIO;
            return FS_FAILURE;
        }
        return (ssize_t)nbyte;
    }
#endif
......
}

ssize_t LOS_Write(int fd, const void *buf, size_t nbyte)
{
#ifdef LOSCFG_RANDOM_DEV
    if (fd == RANDOM_DEV_FD) {
        errno = EBADF; /* "/dev/random" is readonly */
        return FS_FAILURE;
    }
#endif
......
}


审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 文件系统
    +关注

    关注

    0

    文章

    305

    浏览量

    21043
  • 源码
    +关注

    关注

    8

    文章

    689

    浏览量

    31513
  • vfs
    vfs
    +关注

    关注

    0

    文章

    14

    浏览量

    5547
  • 鸿蒙
    +关注

    关注

    60

    文章

    3019

    浏览量

    46178
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    使用“tk(cbc(aes))”进行文件系统加密时出现 iMX8MM CAAM 错误如何解决?

    当使用 CAAM 写入文件系统以进行文件系统加密时,我收到以下错误。tk(cbc(aes))\'。 caam_jr 30902000.jr:4000141c:DECO:desc idx 20:DECO 看门狗定时器超时错误 这种情况只是偶尔发生,但在启用所有
    发表于 03-16 06:24

    虚拟机数据恢复—虚拟机vmdk文件被删除的数据恢复案例

    本次数据恢复涉及一台R710系列服务器和一台MD3200系列存储,上层是ESXI5.5版本的虚拟机和虚拟文件。因客户机房非正常断电,虚拟机无法启动。机房管理员检查发现虚拟机配置
    的头像 发表于 02-10 17:05 509次阅读
    <b class='flag-5'>虚拟</b>机数据恢复—<b class='flag-5'>虚拟</b>机vmdk<b class='flag-5'>文件</b>被删除的数据恢复案例

    开源鸿蒙技术大会2025丨虚拟化与容器分论坛:构筑开源鸿蒙虚拟化技术高地

    随着开源鸿蒙技术的快速发展,各行各业、各品类的生态设备加速融入生态,当数字世界的边界不断拓宽,我们正站在操作系统革命的十字路口。虚拟化技术打破硬件桎梏,‌让一块芯片承载多重宇宙,从智能手表到车载
    的头像 发表于 11-20 17:35 1065次阅读
    开源<b class='flag-5'>鸿蒙</b>技术大会2025丨<b class='flag-5'>虚拟</b>化与容器分论坛:构筑开源<b class='flag-5'>鸿蒙</b><b class='flag-5'>虚拟</b>化技术高地

    明晚8点|睿擎文件系统实战:从开发到发布全流程解析

    文件操作到镜像发布,一次直播掌握完整开发流程!在嵌入式系统开发中,文件系统是数据存储、配置管理和资源访问的核心基础。然而在实际开发中,文件操作效率低下、镜像打包流程复杂、
    的头像 发表于 11-11 11:53 789次阅读
    明晚8点|睿擎<b class='flag-5'>文件系统</b>实战:从开发到发布全流程解析

    【直播预告】下周三晚8点|睿擎文件系统实战:从开发到发布全流程解析

    文件操作到镜像发布,一次直播掌握完整开发流程!在嵌入式系统开发中,文件系统是数据存储、配置管理和资源访问的核心基础。然而在实际开发中,文件操作效率低下、镜像打包流程复杂、
    的头像 发表于 11-06 18:05 2047次阅读
    【直播预告】下周三晚8点|睿擎<b class='flag-5'>文件系统</b>实战:从开发到发布全流程解析

    睿擎派文件系统指南:从开发到发布全流程实践 | 技术解析

    在嵌入式系统开发中,文件系统扮演着至关重要的角色,它负责数据的持久化存储、配置文件管理和资源访问等核心功能。睿擎平台提供了一套完整的文件系统解决方案,从开发阶段的API调用到调试阶段的
    的头像 发表于 11-05 18:13 8301次阅读
    睿擎派<b class='flag-5'>文件系统</b>指南:从开发到发布全流程实践 | 技术解析

    飞凌嵌入式ElfBoard-系统应用编程之文件I/O

    字机制,用于不同进程之间的通信。管道和套接字也被视为文件,并通过文件描述符进行访问。虚拟文件系统(Virtual File System):虚拟文件系统是Linux
    发表于 10-24 08:57

    飞凌嵌入式ElfBoard-Vim编辑器之windows与ubuntu系统互传文件

    如下,点击菜单栏的“虚拟机”,选择“设置”点击“选项”,启用“共享文件夹”,设置Windows主机上的共享目录,点击“确定”。虚拟机的文件共享设置完成后,将测试的所用的:
    发表于 10-20 08:46

    开源鸿蒙技术大会2025丨智能生态分论坛:共绘智能生态新蓝图

    2025年9月27日,开源鸿蒙技术大会智能生态分论坛在长沙国际会议中心成功举办。本次分论坛汇聚了来自操作系统、芯片平台、创新显示、应用开发等领域的数十位顶尖专家和资深实践者,围绕“开源鸿蒙
    的头像 发表于 10-12 21:05 985次阅读
    开源<b class='flag-5'>鸿蒙</b>技术大会2025丨<b class='flag-5'>轻</b>智能生态分论坛:共绘<b class='flag-5'>轻</b>智能生态新蓝图

    Linux三大主流文件系统解析

    还在为选择哪个文件系统而纠结?作为一名摸爬滚打多年的运维老鸟,我将用最接地气的方式,带你彻底搞懂 Linux 三大主流文件系统的奥秘。
    的头像 发表于 08-05 17:37 1747次阅读

    飞凌嵌入式ElfBoard ELF 1板卡-文件系统常用命令之磁盘管理与维护常用命令

    :当前系统在哪个设备的哪个分区,其中tmpfs为虚拟文件系统;Type:文件系统类型;Size:当前系统的大小。1K-blocks对应列的单位是1KB,可通过-h 或者-m 来改变单位
    发表于 06-24 11:26

    飞凌嵌入式ElfBoard ELF 1板卡-busybox构建根文件系统文件系统验证

    至此,我们已经完成了一个最小文件系统的构建,现在我们使用nfs将其挂载为网络文件系统,进行测试。开发板启动之后,在uboot阶段,修改bootargs环境变量。在mmcargs中进行修改:=&
    发表于 06-23 16:10

    飞凌嵌入式ElfBoard ELF 1板卡-文件系统简介

    临时的设备,例如网络文件系统/proc一个虚拟文件系统,为内核提供向进程发送信息的机制。操作系统运行时,进程以及内核信息存放路径/sbin包
    发表于 06-19 17:22

    服务器数据恢复—ocfs2文件系统被格式化为Ext4文件系统的数据恢复案例

    服务器存储数据恢复环境&故障: 人为误操作将Ext4文件系统误装入一台服务器存储上的Ocfs2文件系统数据卷上,导致原Ocfs2文件系统被格式化为Ext4文件系统
    的头像 发表于 06-10 12:03 890次阅读
    服务器数据恢复—ocfs2<b class='flag-5'>文件系统</b>被格式化为Ext4<b class='flag-5'>文件系统</b>的数据恢复案例

    Vsan数据恢复——Vsan分布式文件系统虚拟机不可用的数据恢复

    一台采用VsSAN分布式文件系统的存储设备由于未知原因关机重启。管理员发现上层的虚拟机不可用,存储内的数据丢失。
    的头像 发表于 05-15 17:42 695次阅读
    Vsan数据恢复——Vsan分布式<b class='flag-5'>文件系统</b>上<b class='flag-5'>虚拟</b>机不可用的数据恢复