一、 procfs介绍
procfs是类UNIX操作系统中进程文件系统(process file system)的缩写,主要用于通过内核访问进程信息和系统信息,以及可以修改内核参数改变系统行为。需要注意的是,procfs文件系统是一个虚拟文件系统,不存在硬盘当中,而是系统启动时动态生成的文件系统,储存在内存中。procfs文件系统通常挂载在/proc目录下。
LiteOS-A是OpenAtom OpenHarmony(以下简称“OpenHarmony”)系统中使用的轻量系统内核,实现了procfs文件系统。本文主要对LiteOS-A内核中的procfs文件系统的设计、实现和使用进行介绍和分析。
procfs文件系统是LiteOS-A内核文件系统的一个案例,通过了解procfs文件系统,能够熟悉LiteOS-A的文件系统框架,并很好地将内核信息通过文件系统反馈给使用者。
1. Linux系统中的procfs文件系统包含的内容
Ubuntu 20.04中的/proc文件信息如下:
图1:Ubuntu proc目录信息
2. OS-A系统的命令以及procfs文件系统的内容
LiteOS-A的命令集:
LiteOS-A的proc目录信息如下:
图2:liteOS-A proc目录信息
二、 procfs文件系统的设计
LiteOS-A中使用VFS作为各个文件系统的粘合层,而VFS在OpenHarmony内核中采用树结构实现,树中的每一个节点都是Vnode结构体。VFS提供统一的抽象接口用于屏蔽文件系统之间的差异,其提供三大操作接口用于统一不同文件系统调用不同接口的现状。
VFS提供的三大操作接口:
• VnodeOps
• MountOps
• file_operations_vfs
VnodeOps用于控制Vnode节点,MountOps控制挂载点,file_operations_vfs提供常用的文件接口。
文件系统各自需要实现VFS提供的这三大接口,即实现系统本身需要的接口方法,让VFS能够调用这些接口即可。procfs文件系统虽作为一个伪文件系统pseudo-file system,但其仍旧需要实现上述接口。
1. VFS提供的重要接口
(1) Vnode 结构体:
struct Vnode {
enum VnodeType type; /* Vnode节点类型 */
int useCount; /* 节点链接数 */
uint32_t hash; /* 哈希值 */
uint uid; /* 文件拥有者的user id */
uint gid; /* 文件群组id */
mode_t mode; /* 文件读写执行权限 */
LIST_HEAD parentPathCaches; /* 指向父节点的路径缓存 */
LIST_HEAD childPathCaches; /* 指向儿子节点的路径缓存 */
struct Vnode *parent; /* vnode父节点 */
struct VnodeOps *vop; /* vnode操作接口 */
struct file_operations_vfs *fop; /* 文件操作接口,即指定文件系统 */
void *data; /* 数据,指向内部数据的指针 */
uint32_t flag; /* 节点标签 */
LIST_ENTRY hashEntry; /* 挂入v_vnodeHashEntry[i]中 */
LIST_ENTRY actFreeEntry; /* 通过本节点挂载到空闲和使用链表中 */
struct Mount *originMount; /* 所在文件系统挂载信息 */
struct Mount *newMount; /* 其他挂载在这个节点中的文件系统信息 */
char *filePath; /* Vnode的路径信息 */
struct page_mapping mapping; /* page mapping of the vnode */
};
图3:Vnode structure
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);// 查询节点
int (*Open)(struct Vnode *vnode, int fd, int mode, int flags);// 打开节点
ssize_t (*ReadPage)(struct Vnode *vnode, char *buffer, off_t pos);
ssize_t (*WritePage)(struct Vnode *vnode, char *buffer, off_t pos, size_t buflen);
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);// 创建目录节点
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);// 改变节点属性
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);