• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 虚拟文件系统
2
3
4## 基本概念
5
6VFS(Virtual File System)是文件系统的虚拟层,它不是一个实际的文件系统,而是一个异构文件系统之上的软件粘合层,为用户提供统一的类Unix文件操作接口。由于不同类型的文件系统接口不统一,若系统中有多个文件系统类型,访问不同的文件系统就需要使用不同的非标准接口。而通过在系统中添加VFS层,提供统一的抽象接口,屏蔽了底层异构类型的文件系统的差异,使得访问文件系统的系统调用不用关心底层的存储介质和文件系统类型,提高开发效率。
7
8OpenHarmony内核中,VFS框架是通过在内存中的树结构来实现的,树的每个结点都是一个Vnode结构体,父子结点的关系以PathCache结构体保存。VFS最主要的两个功能是:
9
10- 查找节点。
11
12- 统一调用(标准)。
13
14
15## 运行机制
16
17当前,VFS层主要通过函数指针,实现对不同文件系统类型调用不同接口实现标准接口功能;通过Vnode与PathCache机制,提升路径搜索以及文件访问的性能;通过挂载点管理进行分区管理;通过FD管理进行进程间FD隔离等。下面将对这些机制进行简要说明。
18
191. 文件系统操作函数指针:VFS层通过函数指针的形式,将统一调用按照不同的文件系统类型,分发到不同文件系统中进行底层操作。各文件系统的各自实现一套Vnode操作、挂载点操作以及文件操作接口,并以函数指针结构体的形式存储于对应Vnode、挂载点、File结构体中,实现VFS层对下访问。
20
212. Vnode:Vnode是具体文件或目录在VFS层的抽象封装,它屏蔽了不同文件系统的差异,实现资源的统一管理。Vnode节点主要有以下几种类型:
22   - 挂载点:挂载具体文件系统,如/、/storage
23   - 设备节点:/dev目录下的节点,对应于一个设备,如/dev/mmcblk0
24   - 文件/目录节点:对应于具体文件系统中的文件/目录,如/bin/init
25
26   Vnode通过哈希以及LRU机制进行管理。当系统启动后,对文件或目录的访问会优先从哈希链表中查找Vnode缓存,若缓存没有命中,则并从对应文件系统中搜索目标文件或目录,创建并缓存对应的Vnode。当Vnode缓存数量达到上限时,将淘汰长时间未访问的Vnode,其中挂载点Vnode与设备节点Vnode不参与淘汰。当前系统中Vnode的规格默认为512,该规格可以通过LOSCFG_MAX_VNODE_SIZE进行配置。Vnode数量过大,会造成较大的内存占用;Vnode数量过少,则会造成搜索性能下降。下图展示了Vnode的创建流程。
27
28     **图1** Vnode创建流程
29
30     ![zh-cn_image_0000001127393126](figures/zh-cn_image_0000001127393126.png)
31
321. PathCache:PathCache是路径缓存,它通过哈希表存储,利用父节点Vnode的地址和子节点的文件名,可以从PathCache中快速查找到子节点对应的Vnode。下图展示了文件/目录的查找流程。
33
34     **图2** 文件查找流程
35
36     ![zh-cn_image_0000001175795145](figures/zh-cn_image_0000001175795145.png)
37
381. PageCache:PageCache是内核中文件的缓存。当前PageCache仅支持缓存二进制文件,在初次访问文件时通过mmap映射到内存中,下次再访问时,直接从PageCache中读取,可以提升对同一个文件的读写速度。另外基于PageCache可实现以文件为基底的进程间通信。
39
402. fd管理:Fd(File Descriptor)是描述一个打开的文件/目录的描述符。当前OpenHarmony内核中,fd总规格为896,分为三种类型:
41   - 普通文件描述符,系统总规格为512。
42   - Socket描述符,系统总规格为128。
43   - 消息队列描述符,系统总规格为256。
44
45   当前OpenHarmony内核中,对不同进程中的fd进行隔离,即进程只能访问本进程的fd,所有进程的fd映射到全局fd表中进行统一分配管理。进程的文件描述符最多有256个。
46
473. 挂载点管理:当前OpenHarmony内核中,对系统中所有挂载点通过链表进行统一管理。挂载点结构体中,记录了该挂载分区内的所有Vnode。当分区卸载时,会释放分区内的所有Vnode。
48
49
50## 开发指导
51
52
53### 接口说明
54
55当前文件系统支持的接口如下表所示,表格中的“×”代表对应文件系统不支持该接口。
56
57  **表1** 文件操作
58
59| 接口**名称** | 功能 | FAT | JFFS2 | NFS | TMPFS | PROCFS |
60| -------- | -------- | -------- | -------- | -------- | -------- | -------- |
61| open | 打开文件 | √ | √ | √ | √ | √ |
62| read/pread/readv/preadv | 读取文件 | √ | √ | √ | √ | √ |
63| write/pwrite/writev/pwritev | 写入文件 | √ | √ | √ | √ | √ |
64| lseek | 设置文件偏移 | √ | √ | √ | √ | × |
65| close | 关闭文件 | √ | √ | √ | √ | √ |
66| unlink | 删除文件 | √ | √ | √ | √ | × |
67| fstat | 查询文件信息 | √ | √ | √ | √ | √ |
68| fallocate | 预分配大小 | √ | × | × | × | × |
69| truncate | 文件截断 | √ | √ | × | √ | × |
70| link | 创建硬链接 | × | √ | × | × | × |
71| symlink | 创建软链接 | √ | √ | × | × | × |
72| readlink | 读取软链接 | √ | √ | × | × | × |
73| dup | 复制文件句柄 | √ | √ | √ | √ | √ |
74| fsync | 文件内容刷入设备 | √ | × | × | × | × |
75| ioctl | 设备控制 | × | × | × | √ | × |
76| fcntl | 文件控制操作 | √ | √ | √ | √ | √ |
77| mkdir | 创建目录 | √ | √ | √ | √ | × |
78| opendir | 打开目录 | √ | √ | √ | √ | √ |
79| readdir | 读取目录 | √ | √ | √ | √ | √ |
80| closedir | 关闭目录 | √ | √ | √ | √ | √ |
81| telldir | 获取目录偏移 | √ | √ | √ | √ | √ |
82| seekdir | 设置目录偏移 | √ | √ | √ | √ | √ |
83| rewinddir | 重置目录偏移 | √ | √ | √ | √ | × |
84| scandir | 读取目录数据 | √ | √ | √ | √ | √ |
85| rmdir | 删除目录 | √ | √ | √ | √ | × |
86| chdir | 切换当前路径 | √ | √ | √ | √ | √ |
87| getcwd | 获取当前路径 | √ | √ | √ | √ | √ |
88| realpath | 相对/绝对路径转换 | √ | √ | √ | √ | √ |
89| rename | 文件/目录重命名 | √ | √ | √ | √ | × |
90| chmod | 修改文件/目录属性 | √ | √ | × | × | × |
91| chown | 修改文件/目录所有者 | √ | √ | × | × | × |
92| stat/lstat | 查询文件/目录信息 | √ | √ | √ | √ | √ |
93| access | 查询文件/目录访问权限 | √ | √ | √ | √ | √ |
94| mount | 挂载分区 | √ | √ | √ | √ | √ |
95| umount | 卸载分区 | √ | √ | √ | √ | × |
96| statfs | 查询挂载分区信息 | √ | √ | √ | √ | √ |
97| format | 格式化分区 | √ | × | × | × | × |
98| sync | 分区内容刷入设备 | √ | × | × | × | × |
99
100  **表2** 目录操作
101
102| 接口**名称** | 功能 | FAT | JFFS2 | NFS | TMPFS | PROCFS |
103| -------- | -------- | -------- | -------- | -------- | -------- | -------- |
104| mkdir | 创建目录 | √ | √ | √ | √ | × |
105| opendir | 打开目录 | √ | √ | √ | √ | √ |
106| readdir | 读取目录 | √ | √ | √ | √ | √ |
107| closedir | 关闭目录 | √ | √ | √ | √ | √ |
108| telldir | 获取目录偏移 | √ | √ | √ | √ | √ |
109| seekdir | 设置目录偏移 | √ | √ | √ | √ | √ |
110| rewinddir | 重置目录偏移 | √ | √ | √ | √ | × |
111| scandir | 读取目录数据 | √ | √ | √ | √ | √ |
112| rmdir | 删除目录 | √ | √ | √ | √ | × |
113| chdir | 切换当前路径 | √ | √ | √ | √ | √ |
114| getcwd | 获取当前路径 | √ | √ | √ | √ | √ |
115| realpath | 相对/绝对路径转换 | √ | √ | √ | √ | √ |
116| rename | 文件/目录重命名 | √ | √ | √ | √ | × |
117| chmod | 修改文件/目录属性 | √ | √ | × | × | × |
118| chown | 修改文件/目录所有者 | √ | √ | × | × | × |
119| stat/lstat | 查询文件/目录信息 | √ | √ | √ | √ | √ |
120| access | 查询文件/目录访问权限 | √ | √ | √ | √ | √ |
121| mount | 挂载分区 | √ | √ | √ | √ | √ |
122| umount | 卸载分区 | √ | √ | √ | √ | × |
123| statfs | 查询挂载分区信息 | √ | √ | √ | √ | √ |
124| format | 格式化分区 | √ | × | × | × | × |
125| sync | 分区内容刷入设备 | √ | × | × | × | × |
126
127  **表3** 分区操作
128
129| 接口**名称** | 功能 | FAT | JFFS2 | NFS | TMPFS | PROCFS |
130| -------- | -------- | -------- | -------- | -------- | -------- | -------- |
131| mount | 挂载分区 | √ | √ | √ | √ | √ |
132| umount | 卸载分区 | √ | √ | √ | √ | × |
133| statfs | 查询挂载分区信息 | √ | √ | √ | √ | √ |
134| format | 格式化分区 | √ | × | × | × | × |
135| sync | 分区内容刷入设备 | √ | × | × | × | × |
136
137
138### 开发流程
139
140文件系统的主要开发流程包括挂载/卸载分区,以及系列目录/文件操作。
141
142
143### 编程实例
144
145代码实现如下:
146
147
148```
149#include <stdio.h>
150#include <string.h>
151#include "sys/stat.h"
152#include "fcntl.h"
153#include "unistd.h"
154
155#define LOS_OK 0
156#define LOS_NOK -1
157
158int main(void)
159{
160    int ret;
161    int fd = -1;
162    ssize_t len;
163    off_t off;
164    char mntName[20] = "/storage";
165    char devName[20] = "/dev/mmcblk0p0";
166    char dirName[20] = "/storage/test";
167    char fileName[20] = "/storage/test/file.txt";
168    char writeBuf[20] = "Hello OpenHarmony!";
169    char readBuf[20] = {0};
170
171    /* 创建目录“/storage” */
172    ret = mkdir(mntName, 0777);
173    if (ret != LOS_OK) {
174        printf("mkdir failed.\n");
175        return LOS_NOK;
176    }
177
178    /* 挂载设备“/dev/mmcblk0p0”到“/storage” */
179    ret = mount(devName, mntName, "vfat", 0, 0);
180    if (ret != LOS_OK) {
181        printf("mount failed.\n");
182        return LOS_NOK;
183    }
184
185    /* 创建目录“/storage/test” */
186    ret = mkdir(dirName, 0777);
187    if (ret != LOS_OK) {
188        printf("mkdir failed.\n");
189        return LOS_NOK;
190    }
191
192    /* 创建可读写文件“/storage/test/file.txt” */
193    fd = open(fileName, O_RDWR | O_CREAT, 0777);
194    if (fd < 0) {
195        printf("open file failed.\n");
196        return LOS_NOK;
197    }
198
199    /* 将writeBuf中的内容写入文件 */
200    len = write(fd, writeBuf, strlen(writeBuf));
201    if (len != strlen(writeBuf)) {
202        printf("write file failed.\n");
203        return LOS_NOK;
204    }
205
206    /* 将文件内容刷入存储设备中 */
207    ret = fsync(fd);
208    if (ret != LOS_OK) {
209        printf("fsync failed.\n");
210        return LOS_NOK;
211    }
212
213    /* 将读写指针偏移至文件头 */
214    off = lseek(fd, 0, SEEK_SET);
215    if (off != 0) {
216        printf("lseek failed.\n");
217        return LOS_NOK;
218    }
219
220    /* 将文件内容读出至readBuf中,读取长度为readBuf大小 */
221    len = read(fd, readBuf, sizeof(readBuf));
222    if (len != strlen(readBuf)) {
223        printf("read file failed.\n");
224        return LOS_NOK;
225    }
226    printf("%s\n", readBuf);
227
228    /* 关闭文件 */
229    ret = close(fd);
230    if (ret != LOS_OK) {
231        printf("close failed.\n");
232        return LOS_NOK;
233    }
234
235    /* 删除文件“/storage/test/file.txt” */
236    ret = unlink(fileName);
237    if (ret != LOS_OK) {
238        printf("unlink failed.\n");
239        return LOS_NOK;
240    }
241
242    /* 删除目录“/storage/test” */
243    ret = rmdir(dirName);
244    if (ret != LOS_OK) {
245        printf("rmdir failed.\n");
246        return LOS_NOK;
247    }
248
249    /* 卸载分区“/storage” */
250    ret = umount(mntName);
251    if (ret != LOS_OK) {
252        printf("umount failed.\n");
253        return LOS_NOK;
254    }
255
256    /* 删除目录“/storage” */
257    ret = rmdir(mntName);
258    if (ret != LOS_OK) {
259        printf("rmdir failed.\n");
260        return LOS_NOK;
261    }
262
263    return LOS_OK;
264}
265```
266
267
268**结果验证**
269
270
271编译运行得到的结果为:
272
273
274
275```
276Hello OpenHarmony!
277```
278