fuse接口用法说明
fuse接口用法说明
fuse介绍
fuse即Filesystem in Userspace,用户空间文件系统,可以在应用程序中实现文件系统,能够在用户态使用标准的文件操作,如cat、ls、grep、重定向等功能,虽然效率比内核态要低,但胜在方便。很多场景下用起来还是非常方便的。
fuse本身是内核提供的一个功能,内核开启fuse支持后,会在/dev目录下,生成fuse设备节点,应用层可以通过该设备节点完成用户态文件系统。
libfuse是一个对fuse功能封装的库,提供一系列的api,使用户可以更方便、更简单的使用fuse功能,后面的内容都是基于libfuse来说明的。
目前个人有嵌入式设备上使用fuse的场景,打造一个应用层的proc系统,使用libuse实现起来还是非常方便的,下面主要介绍fuse的基本用法。
libfuse用的还是比较广,它的主页上介绍了一些基于libfuse打造的文件系统,但这个库文档不是很多,文档都是doxygen生成的,也就是说看官方文档,和看头文件差不多,代码里面的注释还是很全面的,实际使用的过程中,看注释也能用,但有些地方还是不容易弄明白。
libfuse的编译
fuse需要内核支持,编译内核时,需把fuse功能选上,fuse可以编译成模块:
File systems —>
< > FUSE (Filesystem in Userspace ) support
fuse驱动加载完成后,可以在/dev/看到创建了一个名为fuse的设备文件,fuse即通过此设备来中转用户态与内核间的信息。
libfuse-fuse-3.2.6.tar.gz需使用meson-0.47.1.tar.gz进行编译,meson又依赖python等,由于个人需求与libfuse版本关系不大,所有用了libfuse-fuse-2.9.8.tar.gz版本
编译过程:
sw@sw:libfuse-fuse-2.9.8$ ./makeconf.sh
sw@sw:libfuse-fuse-2.9.8$ ./configure --host=arm-linux
sw@sw:libfuse-fuse-2.9.8$ make -j4
编译完成后,在lib/.lib/目录下生成了libfuse.a,头文件在include目录下,示例程序在example目录下。
如要查看编译选项,可以使用make V=1编译,这样就可以看到编译选项。
如果需要增加或修改编译选项(如嵌入式可以把O2改成Os,减少库大小),可以在configure.ac中增加,如:
CFLAGS="-Wall -W -Wno-sign-compare -Wstrict-prototypes -Wmissing-declarations -Wwrite-strings -g -O2 -fno-strict-aliasing -ffunction-sections -fdata-sections "
这里增加了 -ffunction-sections -fdata-sections,为了减少应用程序大小(效果不明显),大家可以根据要求改编译选项。
示例程序说明
libfuse提供了几个示例程序,都在example目录下:
- hello.c :最简单的libfuse用法,基本api用法
- hello_ll.c :ll是low level的缩写,展示了低层次接口的基本用法
- null.c :实现了一个类似linux下的/dev/null的设备
- fioc.c/fioclient.c :fioc是fuse ioctl的缩写,这个例子展示的就像很多驱动一样,能够使用ioctl接口进行交互
- fsel.c/fselclient.c : fsel是fuse select缩写,比较高级的用法。
- cusexmp.c/fusexmp.c/fusexmp_fh.c : 这几个例子都是文件映射相关的,有一定的实用意义,实现的接口较全面。
验证测试程序,这里只是看一下基本用法,进到example目录下,先确认一下设备存在
sw@sw:example$ ls /dev/fuse
/dev/fuse
sw@sw:example$ mkdir hfs
sw@sw:example$ ./hello hfs
sw@sw:example$ ls -l hfs/
total 0
-r–r--r-- 1 root root 13 1月 1 1970 hello
sw@sw:example$ cat hfs/hello
Hello World!
使用ps可以看到./hello hfs是在后台运行的,此时hfs不能操作(如进到这个目录创建文件),如想删除hfs文件夹,需先umount
sw@sw:example$ sudo umount hfs
如果umount之前,进程被杀,对该文件操作会报错:Transport endpoint is not connected,此时umount一下就可以了。
这个hello程序也可以前台运行,libfuse支持很多选项,先看一下:
sw@sw:example$ ./hello -h
usage: /home/sw/mypro/libfuse-fuse-2.9.8/example/.libs/lt-hello mountpoint [options]general options:
-o opt,[opt…] mount options
-h --help print help
-V --version print versionFUSE options:
-d -o debug enable debug output (implies -f)
-f foreground operation
-s disable multi-threaded operation-o allow_other allow access to other users
-o allow_root allow access to root
-o auto_unmount auto unmount on process termination
…
libfuse支持很多选项,如
- -o 这个选项就像mount的-o选项一样,可以定义一些挂载选项,
- -d 这是个调试选项,这个选项建议在开发阶段都加上,对调试帮助很大
- -f 这个就是让程序运行到前台的选项,如不使用fuse_main这种最简单的api,选项用处就不大
加上参数-d与-f再次运行hello程序,如下:
sw@sw:example$ ./hello hfs -d -f
FUSE library version: 2.9.8
nullpath_ok: 0
nopath: 0
utime_omit_ok: 0
unique: 1, opcode: INIT (26), nodeid: 0, insize: 56, pid: 0
INIT: 7.23
flags=0x0003f7fb
max_readahead=0x00020000
INIT: 7.19
flags=0x00000011
max_readahead=0x00020000
max_write=0x00020000
max_background=0
congestion_threshold=0
unique: 1, success, outsize: 40unique: 2, opcode: GETATTR (3), nodeid: 1, insize: 56, pid: 11375
getattr /
unique: 2, success, outsize: 120
unique: 3, opcode: GETATTR (3), nodeid: 1, insize: 56, pid: 11379
getattr /
unique: 3, success, outsize: 120
unique: 4, opcode: OPENDIR (27), nodeid: 1, insize: 48, pid: 11379
unique: 4, success, outsize: 32
unique: 5, opcode: READDIR (28), nodeid: 1, insize: 80, pid: 11379
readdir[0] from 0
…
可以看到,有一些调试信息,在其他终端cat一下hello文件,会看到各种opcode(operate code操作码),可以根据此调试信息来决定代码中某接口是否需实现。
此时再ctrl+c退出程序,再用ps查看,可以看到程序已退出了,这就是前台运行。
大家如果有调试测试程序的需求(适用gdb跟踪,熟悉api用法,或看源码),要注意了,测试程序都是用libtool编译的,生成的都是脚本,不能直接调试,大家可以make V=1查看编译选项,如下:
/bin/sh …/libtool --tag=CC --mode=link gcc -Wall -W -Wno-sign-compare -Wstrict-prototypes -Wmissing-declarations -Wwrite-strings -g -O2 -fno-strict-aliasing -o hello hello.o …/lib/libfuse.la
改成
gcc -Wall -W -Wno-sign-compare -Wstrict-prototypes -Wmissing-declarations -Wwrite-strings -g -O2 -fno-strict-aliasing -o hello hello.o …/lib/.libs/libfuse.a -lpthread -ldl
这样就可以编译出hello可执行程序了,需链接pthread和dl库。
libfuse的api使用
libfuse提供了3套不同层次的接口,大家可以根据自己情况选用。
简单(lazy)接口
前面hello例子中用的fuse_main就是最简单的接口,只需要实现一个struct fuse_operations类型的操作函数,就可以完成一个用户态文件系统。用法简单,此接口退出控制比较麻烦,默认捕获了SIGHUP、SIGINT、SIGTERM、SIGPIPE等几个信号,收到这几个信号就退出,不灵活。一般用来功能验证。
基本接口
在fuse.h还提供了一套基本接口,此接口能满足一般程序需求,退出控制较完善,只是初始化去初始化不同,操作函数编程与简单接口一致,适用于简单的应用。函数调用方式与说明如下:
/* fuse_parse_cmdline函数不是必须的,mountpoint可以直接写死目录,可以不从参数里获取 */
int fuse_parse_cmdline(struct fuse_args *args, char **mountpoint, int *multithreaded, int *foreground);/* 创建挂载点,args可以为NULL,返回的是一个fuse所谓的channel */
struct fuse_chan *fuse_mount(const char *mountpoint, struct fuse_args *args);/* 创建fuse操作函数,这里可以传入用户数据 */
struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args,const struct fuse_operations *op, size_t op_size,void *user_data);/* 循环,这个是阻塞的,可以通过fuse_exit退出 */
int fuse_loop(struct fuse *f);/* 释放以上函数资源 */
void fuse_exit(struct fuse *f);
void fuse_unmount(const char *mountpoint, struct fuse_chan *ch);
void fuse_destroy(struct fuse *f);
void fuse_opt_free_args(struct fuse_args *args);
示例程序里并没有基本接口的用法,这里在hello的基础上改了一个测试程序,大家可以参考:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>#define FUSE_USE_VERSION 26
#include "fuse.h"int user_data = 2018;
static const char *hello_str = "Hello World!";
static const char *hello_path = "/hello";void thread_fuse(void * arg)
{struct fuse * fuse = arg;fuse_loop(fuse);
}static int api_getattr(const char *path, struct stat *stbuf)
{int res = 0;memset(stbuf, 0, sizeof(struct stat));if (strcmp(path, "/") == 0) {stbuf->st_mode = S_IFDIR | 0755;stbuf->st_nlink = 2;} else if (strcmp(path, hello_path) == 0) {stbuf->st_mode = S_IFREG | 0444;stbuf->st_nlink = 1;/* 注意,如不清楚具体长度,这个长度需写长一点,否则无法输出 */stbuf->st_size = 1024;} elseres = -ENOENT;return res;
}static int api_readdir(const char *path, void *buf, fuse_fill_dir_t filler,off_t offset, struct fuse_file_info *fi)
{if (strcmp(path, "/") != 0)return -ENOENT;filler(buf, ".", NULL, 0);filler(buf, "..", NULL, 0);filler(buf, hello_path + 1, NULL, 0);return 0;
}static int api_open(const char *path, struct fuse_file_info *fi)
{if ((fi->flags & 3) != O_RDONLY)return -EACCES;/* 参考fuse_file_info结构体的注释,可以在open时填充fh的值,这个就是open的handle,后续read、write中都可以用 */fi->fh = 1234;return 0;
}static int api_read(const char *path, char *buf, size_t size, off_t offset,struct fuse_file_info *fi)
{if(strcmp(path, hello_path) != 0)return -ENOENT;/* 通过fuse_get_context可以取出fuse_new传入的用户参数,该函数在fuse_operations所有的函数中都可以调用 */struct fuse_context * fc = fuse_get_context();int * data = fc->private_data;int len = 0;/* 长度返回0时,本次read就结束了 */if( *data % 2 == 0){len = sprintf(buf,"%s user_data:%d open handle:%lld \n",hello_str,*data,fi->fh);}(*data)++;return len;
}static struct fuse_operations api_oper = {.getattr = api_getattr,.readdir = api_readdir,.open = api_open,.read = api_read,
};#define MOUNT_POINT "hfs"int main()
{char * mountpoint = NULL;char * argv[] = {"usfs", "-d", MOUNT_POINT, NULL};struct fuse_args args = FUSE_ARGS_INIT(3, argv);fuse_parse_cmdline(&args, &mountpoint, NULL, NULL);struct fuse_chan * ch = fuse_mount(mountpoint, &args);struct fuse * fuse = fuse_new(ch, &args, &api_oper, sizeof(struct fuse_operations), &user_data);pthread_t tid_fuse;pthread_create(&tid_fuse, NULL, (void *)thread_fuse, fuse);while (getchar() != 'q'){usleep(1000);}fuse_exit(fuse); /* 退出fuse_loop */fuse_unmount(mountpoint, ch); /* 对应fuse_mount */fuse_destroy(fuse); /* 对应fuse_new */fuse_opt_free_args(&args); /* fuse_parse_cmdline里面会申请内存,需释放 */free(mountpoint);return 0;
}
程序没有错误处理,仅供参考,编译方式如下:
gcc test.c -o test -D_FILE_OFFSET_BITS=64 -I./include -L./lib -lfuse -lpthread -ldl
需用到fuse的头文件和库,头文件在include目录下,库在lib/.libs目录下。
上面例子用到了几个特性:
- 用户参数通过fuse_new传进去,在所有的fuse_operations中,都可以通过fuse_get_context来得到这个user_data的指针
- open操作的时候可以把句柄写入到fh中,后续其他操作可以获取到该fh的值
- read操作,最后一次需return 0,且getattr长度需正确,否则得不到正确的输出
以上测试程序输出如下:
sw@sw:hfs$ cat hello
Hello World! user_data:2024 open handle:1234
sw@sw:hfs$ cat hello
Hello World! user_data:2026 open handle:1234
可以看到user_data两次输出的不一样,在read中输出了open时给的fh值。
此测试程序可以按q退出,完成清理工作。
高级接口
libfuse提供了low level接口,示例程序有几个例子是用该接口实现的。此类接口更灵活,使用稍微复杂一些。libfuse的注释就是文档,非常详细,编程的时候根据注释提示来就可以了。
/*** Read data** Read should send exactly the number of bytes requested except* on EOF or error, otherwise the rest of the data will be* substituted with zeroes. An exception to this is when the file* has been opened in 'direct_io' mode, in which case the return* value of the read system call will reflect the return value of* this operation.** fi->fh will contain the value set by the open method, or will* be undefined if the open method didn't set any value.** Valid replies:* fuse_reply_buf* fuse_reply_iov* fuse_reply_data* fuse_reply_err** @param req request handle* @param ino the inode number* @param size number of bytes to read* @param off offset to read from* @param fi file information*/void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,struct fuse_file_info *fi);
看这个read的注释,read需配合fuse_reply_buf、fuse_reply_iov、fuse_reply_data、fuse_reply_err这几个接口来完成功能,每个fuse_operations的有效接口都不一样,详细的用法大家可以参考人家写好的文件系统。
fuse接口用法说明相关推荐
- php拍照从手机相册中选择,微信js-sdk预览图片接口及从拍照或手机相册中选图接口用法示例...
本文实例讲述了微信js-sdk预览图片接口及从拍照或手机相册中选图接口用法.分享给大家供大家参考,具体如下: 目前中js-sdk 1.0版本中,预览图片提供了2个接口,接口的定义参考官方文档 1.预览 ...
- Java的几种常见接口用法
2019独角兽企业重金招聘Python工程师标准>>> Java的几种常见接口用法 今天在看阎宏的< Java与模式>,里面对 java的 几种 接口的常用方法的总结: ...
- Android中Parcelable接口用法
-- 通过writeToParcel将你的对象映射成Parcel对象,再通过createFromParcel将Parcel对象映射成你的对象.也可以将Parcel看成是一个流,通过writeToPa ...
- php接口调用实例化,PHP抽象类和接口用法实例详解
本文实例讲述了PHP抽象类和接口用法.分享给大家供大家参考,具体如下: 前言 对于oop,估计大多数人并不陌生.有些人除PHP外也学习不少其他语言,会发现php的不同之处,可能语法极其丑陋,但并不妨碍 ...
- Spring系列(八):Spring生命周期中BeanPostProcessor接口用法介绍
今天给大家介绍BeanPostProcessor接口用法,希望对大家能有所帮助! 1.BeanPostProcessor 概念介绍 BeanPostProcessor接口通常被称为Bean的后置处理器 ...
- Java语言Socket接口用法详解
Socket接口用法详解 在Java中,基于TCP协议实现网络通信的类有两个,在客户端的Socket类和在服务器端的ServerSocket类,ServerSocket类的功能是建立一个Serve ...
- php单元格字体颜色,PHPExcel API接口用法大全,按模板导入excel,美化excel,导出图片,设置单元格字体颜色背景色边框,合并单元格,设置行高列宽...
PHPExcel API接口用法大全,按模板导入excel,美化excel,导出图片,设置单元格字体颜色背景色边框,合并单元格,设置行高列宽 2020-08-31 85 一:读取excel表模板$ph ...
- Android中Parcelable与Serializable接口用法
转自: Android中Parcelable接口用法 1. Parcelable接口 Interface for classes whose instances can be written to a ...
- 支付宝 自动发货 php,ecshop支付宝自动发货接口用法示例
本文实例讲述了ecshop支付宝自动发货接口用法.分享给大家供大家参考,具体如下: 一.在数据库order_info中添加trade_no 字段 SQL语句: 复制代码代码如下: ALTER TABL ...
最新文章
- iis7 您无权使用所提供的凭据查看此目录或页面。_使用Spring Cloud和Docker构建微服务架构
- 机器学习笔记:Adagrad
- 如何精确评估开发时间的 4 个小套路?
- [GitHub] 75+的 C# 数据结构和算法实现
- python获取手机通知栏消息_Python编写简单的通知栏脚本启动工具
- opencv数字图像处理(2) - 直方图处理方法【直方图均衡化与直方图规定化】
- 在VC下实现串口通讯
- 前端项目:基于Nodejs+vue开发实现酒店管理系统
- 【Go】blockchain-tutorial 区块链技术教程
- PDMS.NET开发
- C++ 相关职位的要求
- IMO 2017 T4解答
- excel两个指标相关性分析_用Excel做相关性分析方法
- 蚂蚁金服 花呗借呗 招聘公告
- MIPI换EDP芯片-LT8911EXB芯片,商显行业新选择
- 武汉轻工大学计算机学院宿舍,武汉轻工大学有几个校区及校区地址 哪个校区最好...
- 穷人跟懒人 富人跟勤快人
- Ubuntu 18.04配置及美化 (20.04 / 22.04基本相同)
- el-select如何选择整个对象item
- 《预言者》:从路人甲到教父的预言之路