当前位置: 首页 > 科技观察

鸿蒙轻内核M核心源码分析系列之虚拟文件系统VFS

时间:2023-03-22 11:01:04 科技观察

更多内容请访问:鸿蒙技术社区与华为官方共建https://harmonyos.51cto.comVFS(VirtualFileSystem)是虚拟的文件系统层并不是一个实际的文件系统,而是在异构文件系统之上的软件胶层,为用户提供统一的类Unix文件操作接口。由于不同类型文件系统的接口并不统一,如果系统中存在多种类型的文件系统,则需要使用不同的非标准接口来访问不同的文件系统。通过在系统中加入VFS层,提供统一的抽象接口,屏蔽了底层异构文件系统的差异,使得访问文件系统的系统调用不需要关心底层存储介质和文件系统类型,提高开发效率。本文首先介绍了VFS结构和全局变量,然后详细分析了VFS文件操作接口。本文涉及的源代码可以从开源站点https://gitee.com/openharmony/kernel_liteos_m获取。1.VFS结构定义VFS虚拟文件系统操作涉及的结构定义在文件components\fs\vfs\fs_operations.h中。⑴处的structMountOps结构封装了挂载相关的操作,包括挂载、卸载、文件系统统计等操作。(2)处的structFsMap结构映射了文件系统类型及其对应的挂载操作和文件系统操作。支持的文件类型包括“fat”和“littlefs”。通过这个结构体,可以获得相应文件类型操作的挂载和文件系统操作接口。⑶处的structFileOps封装了文件系统的操作接口,包括相应的文件操作、目录操作、统计等接口。⑴structMountOps{int(*Mount)(constchar*source,constchar*target,constchar*filesystemtype,unsignedlongmountflags,constvoid*data);int(*Umount)(constchar*target);int(*Umount2)(constchar*target,intflag);int(*Statfs)(constchar*path,structstatfs*buf);};⑵structFsMap{constchar*fileSystemtype;conststructMountOps*fsMops;conststructFileOps*fsFops;};⑶structFileOps{int(*Open)(constchar*path,intopenFlag,...);int(*Close)(intfd);int(*Unlink)(constchar*fileName);int(*Rmdir)(constchar*dirName);int(*Mkdir)(constchar*dirName,mode_tmode);structdirent*(*Readdir)(DIR*dir);DIR*(*Opendir)(constchar*dirName);int(*Closedir)(DIR*dir);int(*Read)(intfd,void*buf,size_tlen);int(*写入)(intfd,constvoid*buf,size_tlen);off_t(*Seek)(intfd,off_toffset,intwhence);int(*Getattr)(constchar*path,structstat*buf);int(*Rename)(constchar*oldName,constchar*newName);int(*Fsync)(intfd);int(*Fstat)(intfd,structstat*buf);int(*Stat)(constchar*path,structstat*buf);int(*Ftruncate)(intfd,off_tlength);};2.components\fs\vfs\los_fs.c文件中有两个重要的VFS内部全局变量。(1)处定义的数组g_fsmap维护文件系统类型映射信息。数组大小为2,支持“fat”和“littlefs”文件类型的变量g_fs(2)根据挂载的文件类型指向数组g_fsmap中的FsMap类型元素。⑶函数InitMountInfo()会初始化并赋值数组g_fsmap。第0个元素维护的“fat”文件类型的文件系统映射信息,第一个元素维护的“littlefs”文件类型的文件系统映射信息。在相应的文件系统文件中定义了相关的挂载操作和文件系统操作变量g_fatfsMnt、g_fatfsFops、g_lfsMnt、g_lfsFops。(4)处的函数MountFindfs()用于根据文件类型从数组中获取文件映射信息。⑴staticstructFsMapg_fsmap[MAX_FILESYSTEM_LEN]={0};⑵staticstructFsMap*g_fs=NULL;⑶staticvoidInitMountInfo(void){#if(LOSCFG_SUPPORT_FATFS==1)externstructMountOpsg_fatfsMnt;externstructFileOpsg_fatfsFops;g_fsmap=[d].g_fsmap[0].fileSystem.fsMops=&g_fatfsMnt;g_fsmap[0].fsFops=&g_fatfsFops;#endif#if(LOSCFG_SUPPORT_LITTLEFS==1)externstructMountOpsg_lfsMnt;externstructFileOpsg_lfsFops;g_fsmap[1].fileSystemtype=strdup("littlefs_lf);ps&fgsmap[1].g_fsmap=gfsFops=&g_lfsFops;#endif}⑷staticstructFsMap*MountFindfs(constchar*fileSystemtype){structFsMap*m=NULL;for(inti=0;ifileSystemtype&&strcmp(fileSystemtype,m->fileSystemtype)==0){returnm;}}returnNULL;}3.VFS相关的操作接口在之前的系列文章中有介绍《鸿蒙轻内核M核源码分析系列十九 Musl LibC》,该接口会调用VFS中的操作接口文件系统,各个接口的用途和用法不再赘述,快速记录各个操作接口。3.1Mount和unmount操作mount和unmount操作包括三个操作:LOS_FsMount、LOS_FsUmount和LOS_FsUmount2。⑴挂载文件系统前,需要初始化文件系统映射信息,只会做一次。(2)根据文件系统类型获取对应的文件类型映射信息。从这里我们可以知道,LiteOS-M内核同时只能支持一个文件系统,不仅是fat,还有littlefs。⑶对应相应的文件系统挂载接口,实现挂载操作。另外两个函数也比较简单,自己看代码就可以了。intLOS_FsMount(constchar*source,constchar*target,constchar*filesystemtype,unsignedlongmountflags,constvoid*data){staticintinitFlags=0;oneif(initFlags==0){InitMountInfo()initFlags=1;}b_fs=MountFindfs(文件系统类型);如果(g_fs==NULL){errno=ENODEV;returnFS_FAILURE;}if(g_fs->fsMops==NULL||g_fs->fsMops->Mount==NULL){errno=ENOSYS;returnFS_FAILURE;}7return_fs->fsMops->Mount(source,target,filesystemtype,mountflags,data);}intLOS_FsUmount(constchar*target){if(g_fs==NULL){errno=ENODEV;返回FS_FAILURE;}if(g_fs->fsMops==NULL||g_fs->fsMops->Umount==NULL){errno=ENOSYS;returnFS_FAILURE;}return_fs->fsMops->Umount(target);}intLOS_FsUmount2(constchar*target,intflag){if(g_fs==NULL){errno=ENODEV;returnFS_FAILURE;}if(g_fs->fsMops==NULL||g_fs->fsMops->Umount2==NULL){errno=ENOSYS;returnFS_FAILURE;}return_fs->fsMops->Umount2(target,flag);VFS还生成一系列键,包括LOS_Open、LOS_Close、LOS_Read、LOS_Write、LOS_Opendir、LOS_Readdir、LOS_Closedir等封装了具体文件类型的文件目录操作接口。代码比较简单,可以自己阅读。部分代码片段如下:...intLOS_Unlink(constchar*path){if(g_fs==NULL){errno=ENODEV;returnFS_FAILURE;}if(g_fs->fsFops==NULL||g_fs->fsFops->Unlink==NULL){errno=ENOSYS;returnFS_FAILURE;}return_fs->fsFops->Unlink(path);}intLOS_Fstat(intfd,structstat*buf){if(g_fs==NULL){errno=ENODEV;returnFS_FAILURE;}if(g_fs->fsFops==NULL||g_fs->fsFops->Fstat==NULL){errno=ENOSYS;returnFS_FAILURE;}returng_fs->fsFops->Fstat(fd,buf);}......intLOS_Mkdir(constchar*路径,mode_tmode){if(g_fs==NULL){errno=ENODEV;returnFS_FAILURE;}if(g_fs->fsFops==NULL||g_fs->fsFops->Mkdir==NULL){errno=ENOSYS;returnFS_FAILURE;}return_fs->fsFops->Mkdir(path,mode);}DIR*LOS_Opendir(constchar*dirName){if(g_fs==NULL){errno=ENODEV;returnNULL;}if(g_fs->fsFops==NULL||g_fs->fsFops->Opendir==NULL){errno=ENOSYS;returnNULL;}return_fs->fsFops->Opendir(dirName);}......3.3随机数文件/dev/random文件可以用于生成随机数。LiteOS-M在启用宏LOSCFG_RANDOM_DEV时支持随机数文件。从(1)可以看出,随机数依赖于文件~/openharmony/base/security/huks/interfaces/innerkits/huks_lite/hks_client.h和hks_tmp_client.c,这些文件是用来生成随机数的。(2)⑵处定义的RANDOM_DEV_FD和RANDOM_DEV_PATH分别为随机数文件的文件描述符和随机数文件路径。#ifdefLOSCFG_RANDOM_DEV⑴#include"hks_client.h"⑵#defineRANDOM_DEV_FDCONFIG_NFILE_DESCRIPTORS+CONFIG_NSOCKET_DESCRIPTORS#defineRANDOM_DEV_PATH"/dev/random"#endif3.3.1RandomLOS_OpenandLOS_Close该函数用于打开文件并获取文件描述符。⑴表示对于随机数文件,openlabel选项只能支持指定的,否则返回错误码。(2)获取标准路径,获取失败返回错误码。(3)比较得到的标准路径是否为RANDOM_DEV_PATH,当确认为随机数路径时,从(4)处开始判断。如果访问方式为只读,则返回错误,如果打开选项标签为目录,则返回错误。如果不是上述错误情况,则返回一个随机数文件描述符。(5)如果获取到的标准路径为“/”或“/dev”,则根据不同的选项返回不同的错误码。intLOS_Open(constchar*path,intoflag,...){#ifdefLOSCFG_RANDOM_DEVunsignedflags=O_RDONLY|O_WRONLY|O_RDWR|O_APPEND|O_CREAT|O_LARGEFILE|O_TRUNC|O_EXCL|O_DIRECTORY;⑴if((unsigned)oflag&~flags){errno=EINFAIL;returnFS_FAIL;}size_tpathLen=strlen(path)+1;char*canonicalPath=(char*)malloc(pathLen);if(!canonicalPath){errno=ENOMEM;returnFS_FAILURE;}⑵if(GetCanonicalPath(NULL,path,canonicalPath,pathLen)==0){FREE_AND_SET_NULL(canonicalPath);errno=ENOMEM;returnFS_FAILURE;}⑶if(strcmp(canonicalPath,RANDOM_DEV_PATH)==0){FREE_AND_SET_NULL(canonicalPath);⑷if((O_ACCMODE&(unsigned)oflag)!=O_RDONLY){errno=EPERM;returnFS_FAILURE;}if((unsigned)oflag&O_DIRECTORY){errno=ENOTDIR;returnFS_FAILURE;}returnRANDOM_DEV_FD;}⑸if(strcmp(canonicalPath,"/")==0||strcmp(canonicalPath,"/dev")==0){FREE_AND_SET_NULL(canonicalPath);if((unsigned)oflag&O_DIRECTORY){errno=EPERM;returnFS_FAILURE;}errno=EISDIR;returnFS_FAILURE;}FREE_AND_SET_NULL(canonicalPath);#endif......}对于随机数文件,关闭时直接返回成功,无需额外操作。代码片段如下:intLOS_Close(intfd){#ifdefLOSCFG_RANDOM_DEVif(fd==RANDOM_DEV_FD){returnFS_SUCCESS;}#endif......}3.3.2随机LOS_Read和LOS_Write随机数文件读写使用LOS_Read和LOS_Write接口。读取时,首先检查(1)处的传入参数,如果读取的字节数为0,则返回0;如果读缓存地址为空,返回-1;如果读取的字节大于1024,则使用1024。(2)调用hks_generate_random()生成随机数。由于随机数文件是只读的,尝试写入它会返回-1错误代码。ssize_tLOS_Read(intfd,void*buf,size_tnbyte){#ifdefLOSCFG_RANDOM_DEVif(fd==RANDOM_DEV_FD){⑴if(nbyte==0){returnFS_SUCCESS;}if(buf==NULL){errno=EINVAL;returnFS_FAILURE;}if(nbyte>1024){/*1024,maxrandom_size*/nbyte=1024;/*hks_generate_random:random_sizemust<=1024*/}structhks_blobkey={HKS_BLOB_TYPE_RAW,(uint8_t*)buf,nbyte};⑵if(hks_generate_random(&key){=0)errno=EIO;returnFS_FAILURE;}return(ssize_t)nbyte;}#endif......}ssize_tLOS_Write(intfd,constvoid*buf,size_tnbyte){#ifdefLOSCFG_RANDOM_DEVif(fd==RANDOM_DEV_FD){errno=EBADF;/*”/dev/random"isreadonly*/returnFS_FAILURE;}#endif......}总结本文介绍了VFS结构和全局变量,分析了VFS文件操作界面,同时也分析了随机数文件。更多信息请访问:Harmonyos.51cto.com,与华为官方合作打造的鸿蒙技术社区