鸿蒙内核源码分析(VFS篇) | 文件系统和谐共处的基础

基本概念 | 官方定义

VFS(Virtual File System)是文件系统的虚拟层,它不是一个实际的文件系统,而是一个异构文件系统之上的软件粘合层,为用户提供统一的类Unix文件操作接口。由于不同类型的文件系统接口不统一,若系统中有多个文件系统类型,访问不同的文件系统就需要使用不同的非标准接口。而通过在系统中添加VFS层,提供统一的抽象接口,屏蔽了底层异构类型的文件系统的差异,使得访问文件系统的系统调用不用关心底层的存储介质和文件系统类型,提高开发效率。

OpenHarmony内核中,VFS框架是通过在内存中的树结构来实现的,树的每个结点都是一个Vnode结构体,父子结点的关系以PathCache结构体保存。VFS最主要的两个功能是:

  • 查找节点。
  • 统一调用(标准)。

VFS层具体实现包括四个方面:

  • 通过三大函数指针操作接口,实现对不同文件系统类型调用不同接口实现标准接口功能;
  • 通过VnodePathCache机制,提升路径搜索以及文件访问的性能;
  • 通过挂载点管理进行分区管理;
  • 通过FD管理进行进程间FD隔离等。

三大操作接口

VFS层通过函数指针的形式,将统一调用按照不同的文件系统类型,分发到不同文件系统中进行底层操作。各文件系统的各自实现一套Vnode操作(VnodeOps)、挂载点操作(MountOps)以及文件操作接口(file_operations_vfs),并以函数指针结构体的形式存储于对应Vnode、挂载点、File结构体中,实现VFS层对下访问。这三个接口分别为:

VnodeOps | 操作 Vnode 节点
struct VnodeOps {int (*Create)(struct Vnode *parent, const char *name, int mode, struct Vnode **vnode);//创建节点int (*Lookup)(struct Vnode *parent, const char *name, int len, struct Vnode **vnode);//查询节点//Lookup向底层文件系统查找获取inode信息int (*Open)(struct Vnode *vnode, int fd, int mode, int flags);//打开节点int (*Close)(struct Vnode *vnode);//关闭节点int (*Reclaim)(struct Vnode *vnode);//回收节点int (*Unlink)(struct Vnode *parent, struct Vnode *vnode, const char *fileName);//取消硬链接int (*Rmdir)(struct Vnode *parent, struct Vnode *vnode, const char *dirName);//删除目录节点int (*Mkdir)(struct Vnode *parent, const char *dirName, mode_t mode, struct Vnode **vnode);//创建目录节点/*创建一个目录时,实际做了3件事:在其“父目录文件”中增加一个条目;分配一个inode;再分配一个存储块,用来保存当前被创建目录包含的文件与子目录。被创建的“目录文件”中自动生成两个子目录的条目,名称分别是:“.”和“..”。前者与该目录具有相同的inode号码,因此是该目录的一个“硬链接”。后者的inode号码就是该目录的父目录的inode号码。所以,任何一个目录的"硬链接"总数,总是等于它的子目录总数(含隐藏目录)加2。即每个“子目录文件”中的“..”条目,加上它自身的“目录文件”中的“.”条目,再加上“父目录文件”中的对应该目录的条目。*/int (*Readdir)(struct Vnode *vnode, struct fs_dirent_s *dir);//读目录节点int (*Opendir)(struct Vnode *vnode, struct fs_dirent_s *dir);//打开目录节点int (*Rewinddir)(struct Vnode *vnode, struct fs_dirent_s *dir);//定位目录节点int (*Closedir)(struct Vnode *vnode, struct fs_dirent_s *dir);//关闭目录节点int (*Getattr)(struct Vnode *vnode, struct stat *st);//获取节点属性int (*Setattr)(struct Vnode *vnode, struct stat *st);//设置节点属性int (*Chattr)(struct Vnode *vnode, struct IATTR *attr);//改变节点属性(change attr)int (*Rename)(struct Vnode *src, struct Vnode *dstParent, const char *srcName, const char *dstName);//重命名int (*Truncate)(struct Vnode *vnode, off_t len);//缩减或扩展大小int (*Truncate64)(struct Vnode *vnode, off64_t len);//缩减或扩展大小int (*Fscheck)(struct Vnode *vnode, struct fs_dirent_s *dir);//检查功能int (*Link)(struct Vnode *src, struct Vnode *dstParent, struct Vnode **dst, const char *dstName);int (*Symlink)(struct Vnode *parentVnode, struct Vnode **newVnode, const char *path, const char *target);ssize_t (*Readlink)(struct Vnode *vnode, char *buffer, size_t bufLen);
};

MountOps | 挂载点操作

//挂载操作
struct MountOps {int (*Mount)(struct Mount *mount, struct Vnode *vnode, const void *data);//挂载int (*Unmount)(struct Mount *mount, struct Vnode **blkdriver);//卸载int (*Statfs)(struct Mount *mount, struct statfs *sbp);//统计文件系统的信息,如该文件系统类型、总大小、可用大小等信息
};
file_operations_vfs | 文件操作接口
struct file_operations_vfs 
{int     (*open)(struct file *filep);	//打开文件int     (*close)(struct file *filep);	//关闭文件ssize_t (*read)(struct file *filep, char *buffer, size_t buflen);	//读文件ssize_t (*write)(struct file *filep, const char *buffer, size_t buflen);//写文件off_t   (*seek)(struct file *filep, off_t offset, int whence);//寻找,检索 文件int     (*ioctl)(struct file *filep, int cmd, unsigned long arg);//对文件的控制命令int     (*mmap)(struct file* filep, struct VmMapRegion *region);//内存映射实现<文件/设备 - 线性区的映射>/* The two structures need not be common after this point */#ifndef CONFIG_DISABLE_POLLint     (*poll)(struct file *filep, poll_table *fds);	//轮询接口
#endifint     (*stat)(struct file *filep, struct stat* st);	//统计接口int     (*fallocate)(struct file* filep, int mode, off_t offset, off_t len);int     (*fallocate64)(struct file *filep, int mode, off64_t offset, off64_t len);int     (*fsync)(struct file *filep);ssize_t (*readpage)(struct file *filep, char *buffer, size_t buflen);int     (*unlink)(struct Vnode *vnode);
};

PathCache | 路径缓存

PathCache是路径缓存,它通过哈希表存储,利用父节点Vnode的地址和子节点的文件名,可以从PathCache中快速查找到子节点对应的Vnode。当前PageCache仅支持缓存二进制文件,在初次访问文件时通过mmap映射到内存中,下次再访问时,直接从PageCache中读取,可以提升对同一个文件的读写速度。另外基于PageCache可实现以文件为基底的进程间通信。下图展示了文件/目录的查找流程。

LIST_HEAD g_pathCacheHashEntrys[LOSCFG_MAX_PATH_CACHE_SIZE];	//路径缓存哈希表项
struct PathCache {//路径缓存struct Vnode *parentVnode;    /* vnode points to the cache */	struct Vnode *childVnode;     /* vnode the cache points to */LIST_ENTRY parentEntry;       /* list entry for cache list in the parent vnode */LIST_ENTRY childEntry;        /* list entry for cache list in the child vnode */LIST_ENTRY hashEntry;         /* list entry for buckets in the hash table */uint8_t nameLen;              /* length of path component */
#ifdef LOSCFG_DEBUG_VERSIONint hit;                      /* cache hit count*/
#endifchar name[0];                 /* path component name */
};
//路径缓存初始化
int PathCacheInit(void)
{for (int i = 0; i < LOSCFG_MAX_PATH_CACHE_SIZE; i++) {LOS_ListInit(&g_pathCacheHashEntrys[i]);}return LOS_OK;
}

挂载点管理

当前OpenHarmony内核中,对系统中所有挂载点通过链表进行统一管理。挂载点结构体中,记录了该挂载分区内的所有Vnode。当分区卸载时,会释放分区内的所有Vnode。

static LIST_HEAD *g_mountList = NULL;//挂载链表,上面挂的是系统所有挂载点
struct Mount {LIST_ENTRY mountList;              /* mount list */			 //通过本节点将Mount挂到全局Mount链表上const struct MountOps *ops;        /* operations of mount */ //挂载操作函数	struct Vnode *vnodeBeCovered;      /* vnode we mounted on */ //要被挂载的节点 即 /bin1/vs/sd 对应的 vnode节点struct Vnode *vnodeCovered;        /* syncer vnode */		 //要挂载的节点	即/dev/mmcblk0p0 对应的 vnode节点struct Vnode *vnodeDev;            /* dev vnode */LIST_HEAD vnodeList;               /* list of vnodes */		//链表表头int vnodeSize;                     /* size of vnode list */	//节点数量LIST_HEAD activeVnodeList;         /* list of active vnodes */	//激活的节点链表int activeVnodeSize;               /* szie of active vnodes list *///激活的节点数量void *data;                        /* private data */	//私有数据,可使用这个成员作为一个指向它们自己内部数据的指针uint32_t hashseed;                 /* Random seed for vfs hash */ //vfs 哈希随机种子unsigned long mountFlags;          /* Flags for mount */	//挂载标签char pathName[PATH_MAX];           /* path name of mount point */	//挂载点路径名称  /bin1/vs/sdchar devName[PATH_MAX];            /* path name of dev point */		//设备名称 /dev/mmcblk0p0
};
//分配一个挂载点
struct Mount* MountAlloc(struct Vnode* vnodeBeCovered, struct MountOps* fsop)
{struct Mount* mnt = (struct Mount*)zalloc(sizeof(struct Mount));//申请一个mount结构体内存,小内存分配用 zallocif (mnt == NULL) {PRINT_ERR("MountAlloc failed no memory!\n");return NULL;}LOS_ListInit(&mnt->activeVnodeList);//初始化激活索引节点链表LOS_ListInit(&mnt->vnodeList);//初始化索引节点链表mnt->vnodeBeCovered = vnodeBeCovered;//设备将装载到vnodeBeCovered节点上vnodeBeCovered->newMount = mnt;//该节点不再是虚拟节点,而作为 设备结点
#ifdef LOSCFG_DRIVERS_RANDOM	//随机值	驱动模块HiRandomHwInit();//随机值初始化(VOID)HiRandomHwGetInteger(&mnt->hashseed);//用于生成哈希种子HiRandomHwDeinit();//随机值反初始化
#elsemnt->hashseed = (uint32_t)random(); //随机生成哈子种子
#endifreturn mnt;
}

fd管理 | 两种描述符/句柄的关系

Fd(File Descriptor)是描述一个打开的文件/目录的描述符。当前OpenHarmony内核中,fd总规格为896,分为三种类型:

  • 普通文件描述符,系统总数量为512。
    #define CONFIG_NFILE_DESCRIPTORS    512	// 系统文件描述符数量
  • Socket描述符,系统总规格为128。
    #define LWIP_CONFIG_NUM_SOCKETS         128	//socket链接数量#define CONFIG_NSOCKET_DESCRIPTORS  LWIP_CONFIG_NUM_SOCKETS 
  • 消息队列描述符,系统总规格为256。
    #define CONFIG_NQUEUE_DESCRIPTORS    256

请记住,在OpenHarmony内核中,在不同的层面会有两种文件句柄::

  • 系统文件描述符(sysfd),由内核统一管理,和进程描述符形成映射关系,一个sysfd可以被多个profd映射,也就是说打开一个文件只会占用一个sysfd,但可以占用多个profd,即一个文件被多个进程打开.

  • 进程文件描述符(profd),由进程管理的叫进程文件描述符,内核对不同进程中的fd进行隔离,即进程只能访问本进程的fd.举例说明之间的关系:

    文件            sysfd     profd吃个桃桃.mp4        10    13(A进程)吃个桃桃.mp4        10    3(B进程)容嬷嬷被冤枉.txt    12    3(A进程)容嬷嬷被冤枉.txt    12    3(C进程)
  • 不同进程的相同fd往往指向不同的文件,但有三个fd例外

    • STDIN_FILENO(fd = 0) 标准输入 接收键盘的输入
    • STDOUT_FILENO(fd = 1) 标准输出 向屏幕输出
    • STDERR_FILENO(fd = 2) 标准错误 向屏幕输出
      sysfd和所有的profd的(0,1,2)号都是它们.熟知的 printf 就是向 STDOUT_FILENO中写入数据.
  • 具体涉及结构体

    struct file_table_s {//进程fd <--> 系统FD绑定intptr_t sysFd; /* system fd associate with the tg_filelist index */};//sysFd的默认值是-1struct fd_table_s {//进程fd表结构体unsigned int max_fds;//进程的文件描述符最多有256个struct file_table_s *ft_fds; /* process fd array associate with system fd *///系统分配给进程的FD数组 ,fd 默认是 -1fd_set *proc_fds;	//进程fd管理位,用bitmap管理FD使用情况,默认打开了 0,1,2	       (stdin,stdout,stderr)fd_set *cloexec_fds;sem_t ft_sem; /* manage access to the file table */ //管理对文件表的访问的信号量};struct files_struct {//进程文件表结构体int count;				      //持有的文件数量struct fd_table_s *fdt; //持有的文件表unsigned int file_lock;	//文件互斥锁unsigned int next_fd;	  //下一个fd#ifdef VFS_USING_WORKDIRspinlock_t workdir_lock;	//工作区目录自旋锁char workdir[PATH_MAX];		//工作区路径,最大 256个字符#endif};typedef struct ProcessCB {#ifdef LOSCFG_FS_VFSstruct files_struct *files;        /**< Files held by the process */ //进程所持有的所有文件,注者称之为进程的文件管理器#endif	//每个进程都有属于自己的文件管理器,记录对文件的操作. 注意:一个文件可以被多个进程操作}

解读

  • 鸿蒙的每个进程ProcessCB都有属于自己的进程的文件描述符files_struct,该进程和文件系统有关的信息都由它表达.
  • 搞清楚 files_struct,fd_table_s,file_table_s三个结构体的关系就明白了进度描述符和系统描述符的关系.
  • fd_table_s是由alloc_fd_table分配的一个结构体数组,用于存放进程的文件描述符
        //分配进程文件表,初始化 fd_table_s 结构体中每个数据,包括系统FD(0,1,2)的绑定static struct fd_table_s * alloc_fd_table(unsigned int numbers){struct fd_table_s *fdt;void *data;fdt = LOS_MemAlloc(m_aucSysMem0, sizeof(struct fd_table_s));//申请内存if (!fdt){goto out;}fdt->max_fds = numbers;//最大数量if (!numbers){fdt->ft_fds = NULL;fdt->proc_fds = NULL;return fdt;}data = LOS_MemAlloc(m_aucSysMem0, numbers * sizeof(struct file_table_s));//这是和系统描述符的绑定if (!data){goto out_fdt;}fdt->ft_fds = data;//这其实是个 int[] 数组,for (int i = STDERR_FILENO + 1; i < numbers; i++){fdt->ft_fds[i].sysFd = -1;//默认的系统描述符都为-1,即还没有和任何系统文件描述符绑定}data = LOS_MemAlloc(m_aucSysMem0, sizeof(fd_set));//管理FD的 bitmap if (!data){goto out_arr;}(VOID)memset_s(data, sizeof(fd_set), 0, sizeof(fd_set));fdt->proc_fds = data;alloc_std_fd(fdt);//分配标准的0,1,2系统文件描述符,这样做的结果是任务进程都可以写系统文件(0,1,2)(void)sem_init(&fdt->ft_sem, 0, 1);//互斥量初始化return fdt;out_arr:(VOID)LOS_MemFree(m_aucSysMem0, fdt->ft_fds);out_fdt:(VOID)LOS_MemFree(m_aucSysMem0, fdt);out:return NULL;}
  • file_table_s记录 sysfdprofd的绑定关系.fdt->ft_fds[i].sysFd中的i就是profd

鸿蒙全栈开发全新学习指南

也为了积极培养鸿蒙生态人才,让大家都能学习到鸿蒙开发最新的技术,针对一些在职人员、0基础小白、应届生/计算机专业、鸿蒙爱好者等人群,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线【包含了大厂APP实战项目开发】

本路线共分为四个阶段:

第一阶段:鸿蒙初中级开发必备技能

第二阶段:鸿蒙南北双向高工技能基础:gitee.com/MNxiaona/733GH

第三阶段:应用开发中高级就业技术

第四阶段:全网首发-工业级南向设备开发就业技术:https://gitee.com/MNxiaona/733GH

《鸿蒙 (Harmony OS)开发学习手册》(共计892页)

如何快速入门?

1.基本概念
2.构建第一个ArkTS应用
3.……

开发基础知识:gitee.com/MNxiaona/733GH

1.应用基础知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私保护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

基于ArkTS 开发

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

鸿蒙开发面试真题(含参考答案):gitee.com/MNxiaona/733GH

鸿蒙入门教学视频:

美团APP实战开发教学:gitee.com/MNxiaona/733GH

写在最后

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:gitee.com/MNxiaona/733GH

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/705142.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

学习Nginx(五):虚拟主机配置

核心功能 在演示虚拟主机配置之前&#xff0c;来看一下Nginx配置的核心功能说明。 了解配置更多功能&#xff0c;请查看官方说明&#xff1a; http://nginx.org/en/docs/ngx_core_module.html [rootRockyLinux9 conf]# cat nginx.conf # 核心功能&#xff0c;全局配置 # 设置启…

【多模态】31、Qwen-VL | 一个开源的全能的视觉-语言多模态大模型

文章目录 一、背景二、方法2.1 模型架构2.2 输入和输出2.3 训练 三、效果3.1 Image Caption 和 General Visual Question Answering3.2 Text-oriented Visual Question Answering3.3 Refer Expression Comprehension3.4 视觉-语言任务的少样本学习3.5 真实世界用户行为中的指令…

单位个人如何向期刊投稿发表文章?

在单位担任信息宣传员一职以来,我深感肩上的责任重大。每月的对外信息宣传投稿不仅是工作的核心,更是衡量我们部门成效的重要指标。起初,我满腔热血,以为只要勤勉努力,将精心撰写的稿件投至各大报社、报纸期刊的官方邮箱,就能顺利登上版面,赢得读者的青睐。然而,现实远比理想骨…

Ubuntu 配置Samba

Ubuntu 配置&#xff1a; 安装 Samba &#xff1a; sudo apt-get install samba添加用户并设置密码&#xff08;可与ubuntu用户密码相同方便记忆&#xff09; sudo smbpasswd -a root这里我设置的密码为123456 sudo vi /etc/samba/smb.conf注意这个共享的目录一定要存在\ho…

【qt】纯代码界面设计

界面设计目录 一.界面设计的三种方式1.使用界面设计器2.纯代码界面设计3.混合界面设计 二.纯代码进行界面设计1.代码界面设计的总思路2.创建项目3.设计草图4.添加组件指针5.初始化组件指针6.添加组件到窗口①水平布局②垂直布局③细节点 7.定义槽函数8.初始化信号槽9.实现槽函数…

ubuntu quota配置磁盘配额

安装quota工具:sudo apt-get install quota这条命令会安装quota工具&#xff0c;它用于在Linux系统中管理和强制执行磁盘配额。编辑用户quota:sudo edquota -u <username> /data这条命令会打开默认的文本编辑器&#xff0c;允许你为用户liushenshen在/data文件系统上设置…

yolov9训练自定义数据

1.训练yolov9&#xff0c;先准备好一份自定义数据.。到roboflow下载一份数据&#xff0c;数据格式是yolo格式。 2.到github下载yolov9源码 https://github.com/WongKinYiu/yolov9 3.为了方便配置环境&#xff0c;把代码上传到矩池云上面&#xff0c;使用云服务器 4.执行 pip i…

鸿蒙 装饰器@builder 使用中的问题 以及解决方案

builder装饰器 一 介绍Builder装饰器&#xff1a;自定义构建函数二 问题点三 解决方法四 仓库地址 一 介绍Builder装饰器&#xff1a;自定义构建函数 用于填充UI组件 开发者可以将重复使用的UI元素抽象成一个方法&#xff0c;在build方法里调用。 根据场景分类 组件内自定义构…

STM32F407 2个高级定时器生成2路无刷电机波形以及相电流采集程序(寄存器版)

stm32f407 高级定时1、定时8 生成20k 中心PWM 波形 并分别用其通道4 触发ADC1 ADC2 采样 用于分别两无刷电机foc 电流环控制&#xff0c;ADC1产生50us的电流采集完成中断&#xff0c;用于foc算法周期运算 主要参考高级定时器的寄存器和ADC寄存器 首先&#xff0c;要使用STM32F…

用友GRP-U8 bx_dj_check.jsp SQL注入漏洞复现(XVE-2024-10537)

0x01 免责声明 请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;作者不为此承担任何责任。工具来自网络&#xff0c;安全性自测&#xff0c;如有侵权请联系删…

Anaconda安装-超详细版(2024)

扫盲&#xff1a;先装Python还是先装anaconda? 安装anaconda即可&#xff0c;不需要单独装python anaconda 是一个python的发行版&#xff0c;包括了python和很多常见的软件库, 和一个包管理器conda。 一、下载Anaconda 安装包&#xff08;官网和国内镜像资源&#xff09; …

2024年最新最全面的软件测试面试题(四)

1、在项目中如何保证软件质量? 项目质量不仅仅是某个人或某个团队来保障的&#xff0c;而是整个团队一起努力的结果&#xff0c;因此&#xff0c;在公司级别需要 有一个规范的项目流程。 产品&#xff0c;保证迭代过程中的产品逻辑&#xff0c;对于可能的兼容&#xff0c;升…