鸿蒙轻内核源码分析:虚拟文件系统 VFS
本文分享自华为云社区《鸿蒙轻内核M核源码分析系列二一 01 虚拟文件系统VFS》,作者:zhushy 。
VFS(Virtual File System)是文件系统的虚拟层,它不是一个实际的文件系统,而是一个异构文件系统之上的软件粘合层,为用户提供统一的类 Unix 文件操作接口。由于不同类型的文件系统接口不统一,若系统中有多个文件系统类型,访问不同的文件系统就需要使用不同的非标准接口。而通过在系统中添加 VFS 层,提供统一的抽象接口,屏蔽了底层异构类型的文件系统的差异,使得访问文件系统的系统调用不用关心底层的存储介质和文件系统类型,提高开发效率。本文先介绍下 VFS 的结构体和全局变量,然后详细分析下 VFS 文件操作接口。文中所涉及的源码,均可以在开源站点https://gitee.com/openharmony/kernel_liteos_m 获取。
1、VFS 结构体定义
在文件 components\fs\vfs\fs_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 重要的内部全局变量
在文件 components\fs\vfs\los_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
......
}
小结
本文介绍了 VFS 的结构体和全局变量,分析了下 VFS 文件操作接口,对于随机数文件也进行了分析。时间仓促和能力关系,如有失误,欢迎指正。感谢阅读,如有任何问题、建议,都可以博客下留言给我,谢谢。
参考资料
HarmonyOS Device>文档指南>基础能力-虚拟文件系统
鸿蒙轻内核源码分析:虚拟文件系统 VFS的更多相关文章
- 鸿蒙轻内核源码分析:文件系统LittleFS
摘要:本文先介绍下LFS文件系统结构体的结构体和全局变量,然后分析下LFS文件操作接口. 本文分享自华为云社区<# 鸿蒙轻内核M核源码分析系列二一 02 文件系统LittleFS>,作者: ...
- 鸿蒙轻内核源码分析:文件系统FatFS
摘要:本文为大家介绍FatFS文件系统结构体的结构体和全局变量,并分析FatFS文件操作接口. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列二一 03 文件系统FatFS>,作者:zh ...
- 鸿蒙内核源码分析(根文件系统) | 先挂到`/`上的文件系统 | 百篇博客分析OpenHarmony源码 | v66.01
百篇博客系列篇.本篇为: v66.xx 鸿蒙内核源码分析(根文件系统) | 先挂到/上的文件系统 | 51.c.h.o 文件系统相关篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说一 ...
- 鸿蒙内核源码分析(VFS篇) | 文件系统和谐共处的基础 | 百篇博客分析OpenHarmony源码 | v68.01
子曰:"质胜文则野,文胜质则史.文质彬彬,然后君子." <论语>:雍也篇 百篇博客系列篇.本篇为: v68.xx 鸿蒙内核源码分析(VFS篇) | 文件系统和谐共处的基 ...
- 鸿蒙内核源码分析(索引节点篇) | 谁是文件系统最重要的概念 | 百篇博客分析OpenHarmony源码 | v64.01
百篇博客系列篇.本篇为: v64.xx 鸿蒙内核源码分析(索引节点篇) | 谁是文件系统最重要的概念 | 51.c.h.o 文件系统相关篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么 ...
- 鸿蒙内核源码分析(挂载目录篇) | 为何文件系统需要挂载 | 百篇博客分析OpenHarmony源码 | v65.01
百篇博客系列篇.本篇为: v65.xx 鸿蒙内核源码分析(挂载目录篇) | 为何文件系统需要挂载 | 51.c.h.o 文件系统相关篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说一 ...
- 鸿蒙内核源码分析(文件系统篇) | 用图书管理说文件系统 | 百篇博客分析OpenHarmony源码 | v63.01
百篇博客系列篇.本篇为: v63.xx 鸿蒙内核源码分析(文件系统篇) | 用图书管理说文件系统 | 51.c.h.o 文件系统相关篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说一 ...
- 鸿蒙内核源码分析(字符设备篇) | 字节为单位读写的设备 | 百篇博客分析OpenHarmony源码 | v67.01
百篇博客系列篇.本篇为: v67.xx 鸿蒙内核源码分析(字符设备篇) | 字节为单位读写的设备 | 51.c.h.o 文件系统相关篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说一 ...
- 鸿蒙内核源码分析(文件概念篇) | 为什么说一切皆是文件 | 百篇博客分析OpenHarmony源码 | v62.01
百篇博客系列篇.本篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说一切皆是文件 | 51.c.h.o 本篇开始说文件系统,它是内核五大模块之一,甚至有Linux的设计哲学是" ...
- 鸿蒙内核源码分析(管道文件篇) | 如何降低数据流动成本 | 百篇博客分析OpenHarmony源码 | v70.01
百篇博客系列篇.本篇为: v70.xx 鸿蒙内核源码分析(管道文件篇) | 如何降低数据流动成本 | 51.c.h.o 文件系统相关篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说一 ...
随机推荐
- 1.NoSQL-lesson14-MongoDB核心技术-运维篇
逻辑结构 Mongodb 逻辑结构 MySQL逻辑结构 库database 库 集合(collection) 表 文档(document) 数据行 选择之所以称为为选择,肯定是痛苦的! ------& ...
- git 创建本地分支并关联远程分支
1.查看远程分支 git branch 可以看到,我本地只有dev和master分支.现在同事创建了一个远程分支dev-glq,里面是他的代码.我应该再我本地创建一个分支,并且他的关联远程分支. 2. ...
- 管道channel
管道 go语言中管道底层是一个环形队列(先进先出),写入(send)和 取出(recv)都是从同一个位置按照同一方向顺序执行. sendx表示最后一次插入元素位置,recvx表示最后一次取出元素的位置 ...
- java泛型<? extends E>和<? super E>的区别和适用场景
<? extends E>是Upper Bound(上限)的通配符,用来限制元素类型的上限,如: List<? extends Fruit> fruits; 表示集合中的元素的 ...
- [Python急救站]草莓熊的绘制
草莓熊也是一个热门的图案,今天就用Python绘制一下 import turtle as t # 设置背景颜色,窗口位置以及大小 t.colormode(255) # 颜色模式 t.speed(0) ...
- 深度解析NLP文本摘要技术:定义、应用与PyTorch实战
在本文中,我们深入探讨了自然语言处理中的文本摘要技术,从其定义.发展历程,到其主要任务和各种类型的技术方法.文章详细解析了抽取式.生成式摘要,并为每种方法提供了PyTorch实现代码.最后,文章总结了 ...
- C语言,可爱的小明特别喜欢爬楼梯,他有的时候一次爬一个台阶,有的时候一次爬两个台阶,有的时候一次爬三个台阶。如果这个楼梯有n个台阶,小明一共有多少种爬法呢?n值从键盘输入。
/* 开发者:慢蜗牛 开发时间:2020.5.28 程序功能:小明爬楼梯 */ #include<stdio.h> int taijie(int n); long taijie(int n ...
- 电子元器件工厂的金蝶 ERP 与赛意 WMS 系统数据集成平台进行对接
项目背景 国内某晶振集成电路研发单位,涵盖从产品开发.设计.生产.销售.服务等各个环节.需要全面建成以ERP.WMS.BOM.PLM.DMS.SRM.OA 为核心的企业信息系统,支持研发.生产.营销. ...
- IDEA安装与配置教程
一.下载并安装IDEA 1.下载 1.官网: 下载 IntelliJ IDEA (这里以Windows系统为例,其他系统类似) 2.安装 1.下载完成后,直接点击安装包安装,即可. 2.开始安装,然后 ...
- Typora的安装与使用教程
一.安装 1.下载 下载地址:Typora 官方中文站 二.安装 1.无脑下一步即可. 三.使用教程 1.Typora系统设置 一般导出使用PDF文件比较好,性价比比较高. 2.其他设置 以下所有设置 ...