FUSE简介
FUSE(Filesystem in Userspace,用户空间文件系统)作为类UNIX系统平台上可加载的内核模块,允许非特权用户创建功能完备的文件系统,而不需要重新编译内核。FUSE模块仅仅提供kernel模块的接入口,而本身的主要实现代码位于用户空间中。对于读写虚拟文件系统来说,FUSE是个很好的选择。
FUSE IO流程
内核文件系统
FUSE
内核 FUSE 模块在内核态中间做协议封装和协议解析的工作,它接收从VFS下来的请求并按照 FUSE 协议转发到用户态,然后接收用户态的响应,并随后回复给用户。
内核到用户态穿越
FUSE总结
FUSE框架就是内核开发者为了能在用户态进行文件系统开发的需求开发出来的;
FUSE 框架的3大组件分别是:内核 fuse 模块,用户态 libfuse 库,fusermount 挂载工具;
内核FUSE模块用于接收VFS的请求,并且通过 /dev/fuse 建立的管道,把封装后的请求发往用户态;
libfuse 则是用户态封装用来解析 FUSE 数据包协议的库代码,服务于所有的用户态文件系统;
/dev/fuse 就是连接内核 fuse 和用户态文件系统的纽带;
fusermount 则是用户态文件系统用来挂载的工具
用户态FUSE接口及功能
接口
可以参考linux内核struct fuse_operations操作
struct fuse_operations {
int (*getattr) (const char *, struct stat *);
int (*readlink) (const char *, char *, size_t);
int (*mknod) (const char *, mode_t, dev_t);
int (*mkdir) (const char *, mode_t);
int (*unlink) (const char *);
int (*rmdir) (const char *);
int (*symlink) (const char *, const char *);
int (*rename) (const char *, const char *);
int (*link) (const char *, const char *);
int (*chmod) (const char *, mode_t);
int (*chown) (const char *, uid_t, gid_t);
int (*truncate) (const char *, off_t);
int (*utime) (const char *, struct utimbuf *);
int (*open) (const char *, struct fuse_file_info *);
int (*read) (const char *, char *, size_t, off_t, structfuse_file_info *);
int (*write) (const char *, const char *, size_t, off_t, structfuse_file_info *);
int (*statfs) (const char *, struct statvfs *);
int (*flush) (const char *, struct fuse_file_info *);
int (*release) (const char *, struct fuse_file_info *);
int (*fsync) (const char *, int, struct fuse_file_info *);
int (*setxattr) (const char *, const char *, const char *, size_t,int);
int (*getxattr) (const char *, const char *, char *, size_t);
int (*listxattr) (const char *, char *, size_t);
int (*removexattr) (const char *, const char *);
int (*opendir) (const char *, struct fuse_file_info *);
int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t,struct fuse_file_info *);
int (*releasedir) (const char *, struct fuse_file_info *);
int (*fsyncdir) (const char *, int, struct fuse_file_info *);
void *(*init) (struct fuse_conn_info *conn);
void (*destroy) (void *);
int (*access) (const char *, int);
int (*create) (const char *, mode_t, struct fuse_file_info*);
int (*ftruncate) (const char *, off_t, struct fuse_file_info*);
int (*fgetattr) (const char *, struct stat *, struct fuse_file_info*);
int (*lock) (const char *, struct fuse_file_info *, int cmd, structflock *);
int (*utimens) (const char *, const struct timespec tv[2]);
int (*bmap) (const char *, size_t blocksize, uint64_t *idx);
};
功能
getattr: int (*getattr) (const char *, struct stat *); 这个函数与 stat() 类似。st_dev 和 st_blksize 域都可以忽略。st_ino 域也会被忽略,除非在执行mount 时指定了 use_ino 选项。
readlink: int (*readlink) (const char *, char *, size_t); 这个函数会读取一个符号链接的目标。缓冲区应该是一个以 null 结束的字符串。缓冲区的大小参数包括这个 null结束字符的空间。如果链接名太长,不能保存到缓冲区中,就应该被截断。成功时的返回值应该是“0”。
getdir: int (*getdir) (const char *, fuse_dirh_t,fuse_dirfil_t); 这个函数会读取一个目录中的内容。这个操作实际上是在一次调用中执行 opendir()、readdir()、...、closedir()序列。对于每个目录项来说,都应该调用 filldir() 函数。
mknod: int (*mknod) (const char *, mode_t, dev_t); 这个函数会创建一个文件节点。此处没有 create() 操作;mknod()会在创建非目录、非符号链接的节点时调用。
mkdir: int (*mkdir) (const char *, mode_t); 分别用来创建一个目录。
rmdir: int (*rmdir) (const char *); 分别用来删除一个目录。
unlink: int (*unlink) (const char *); 删除名一个文件
rename: int (*rename) (const char *, const char *); 重命名一个文件。
symlink: int (*symlink) (const char *, const char *); 这个函数用来创建一个符号链接。
link: int (*link) (const char *, const char *); 这个函数创建一个到文件的硬链接。
chmod: int (*chmod) (const char *, mode_t); 修改权限位
chown: int (*chown) (const char *, uid_t, gid_t); 修改属主和用户
truncate: int (*truncate) (const char *, off_t); 截断文件
utime: int (*utime) (const char *, struct utimbuf *); 修改文件的访问/修改时间
open: int (*open) (const char *, struct fuse_file_info *); 这是文件的打开操作。对 open()函数不能传递创建或截断标记(O_CREAT、O_EXCL、O_TRUNC)。这个函数应该检查是否允许执行给定的标记的操作。另外,open()也可能在 fuse_file_info结构中返回任意的文件句柄,这会传递给所有的文件操作。
read: int (*read) (const char *, char *, size_t, off_t, structfuse_file_info *); 这个函数从一个打开文件中读取数据。除非碰到 EOF 或出现错误,否则 read()应该返回所请求的字节数的数据;否则,其余数据都会被替换成 0。一个例外是在执行 mount 命令时指定了 direct_io选项,在这种情况中 read() 系统调用的返回值会影响这个操作的返回值。
write: int (*write) (const char *, const char *, size_t, off_t,struct fuse_file_info *); 这个函数将数据写入一个打开的文件中。除非碰到 EOF 或出现错误,否则 write() 应该返回所请求的字节数的数据。一个例外是在执行mount 命令时指定了 direct_io 选项(这于 read()操作的情况类似)。
statfs: int (*statfs) (const char *, struct statfs *); 这个函数获取文件系统的统计信息。f_type 和 f_fsid 域都会被忽略。
flush: int (*flush) (const char *, struct fuse_file_info*); 这表示要刷新缓存数据。它并不等于 fsync() 函数 —— 也不是请求同步脏数据。每次对一个文件描述符执行 close()函数时,都会调用 flush();因此如果文件系统希望在 close()中返回写错误,并且这个文件已经缓存了脏数据,那么此处就是回写数据并返回错误的好地方。由于很多应用程序都会忽略 close()错误,因此这通常用处不大。 注意:我们也可以对一个 open() 多次调用 flush() 方法。如果由于调用了 dup()、dup2() 或 fork()而产生多个文件描述符指向一个打开文件的情况,就可能会需要这种用法。我们无法确定哪个 flush 操作是最后一次操作,因此每个flush 都应该同等地对待。多个写刷新序列相当罕见,因此这并不是什么问题。
release: int (*release) (const char *, struct fuse_file_info*); 这个函数释放一个打开文件。release() 是在对一个打开文件没有其他引用时调用的 ——此时所有的文件描述符都会被关闭,所有的内存映射都会被取消。对于每个 open() 调用来说,都必须有一个使用完全相同标记和文件描述符的release() 调用。对一个文件打开多次是可能的,在这种情况中只会考虑最后一次release,然后就不能再对这个文件执行更多的读/写操作了。release的返回值会被忽略。
fsync: int (*fsync) (const char *, int, struct fuse_file_info*); 这个函数用来同步文件内容。如果 datasync 参数为非0,那么就只会刷新用户数据,而不会刷新元数据。
setxattr: int (*setxattr) (const char *, const char *, const char*, size_t, int); 设置扩展属性
getxattr: int (*getxattr) (const char *, const char *, char *,size_t); 获取扩展属性
listxattr: int (*listxattr) (const char *, char *, size_t); 列出扩展属性
removexattr: int (*removexattr) (const char *, const char*); 删除扩展属性
参考文献
https://www.a-programmer.top/2021/10/05/FUSE%E4%BB%8B%E7%BB%8D/文章来源:https://www.toymoban.com/news/detail-785904.html
https://blog.csdn.net/sourthstar/article/details/8551993文章来源地址https://www.toymoban.com/news/detail-785904.html
到了这里,关于JuiceFS:用户态fuse接口的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!