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 version

FUSE 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: 40

unique: 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接口用法说明相关推荐

  1. php拍照从手机相册中选择,微信js-sdk预览图片接口及从拍照或手机相册中选图接口用法示例...

    本文实例讲述了微信js-sdk预览图片接口及从拍照或手机相册中选图接口用法.分享给大家供大家参考,具体如下: 目前中js-sdk 1.0版本中,预览图片提供了2个接口,接口的定义参考官方文档 1.预览 ...

  2. Java的几种常见接口用法

    2019独角兽企业重金招聘Python工程师标准>>> Java的几种常见接口用法 今天在看阎宏的< Java与模式>,里面对 java的 几种 接口的常用方法的总结: ...

  3. Android中Parcelable接口用法

    --  通过writeToParcel将你的对象映射成Parcel对象,再通过createFromParcel将Parcel对象映射成你的对象.也可以将Parcel看成是一个流,通过writeToPa ...

  4. php接口调用实例化,PHP抽象类和接口用法实例详解

    本文实例讲述了PHP抽象类和接口用法.分享给大家供大家参考,具体如下: 前言 对于oop,估计大多数人并不陌生.有些人除PHP外也学习不少其他语言,会发现php的不同之处,可能语法极其丑陋,但并不妨碍 ...

  5. Spring系列(八):Spring生命周期中BeanPostProcessor接口用法介绍

    今天给大家介绍BeanPostProcessor接口用法,希望对大家能有所帮助! 1.BeanPostProcessor 概念介绍 BeanPostProcessor接口通常被称为Bean的后置处理器 ...

  6. Java语言Socket接口用法详解

    Socket接口用法详解   在Java中,基于TCP协议实现网络通信的类有两个,在客户端的Socket类和在服务器端的ServerSocket类,ServerSocket类的功能是建立一个Serve ...

  7. php单元格字体颜色,PHPExcel API接口用法大全,按模板导入excel,美化excel,导出图片,设置单元格字体颜色背景色边框,合并单元格,设置行高列宽...

    PHPExcel API接口用法大全,按模板导入excel,美化excel,导出图片,设置单元格字体颜色背景色边框,合并单元格,设置行高列宽 2020-08-31 85 一:读取excel表模板$ph ...

  8. Android中Parcelable与Serializable接口用法

    转自: Android中Parcelable接口用法 1. Parcelable接口 Interface for classes whose instances can be written to a ...

  9. 支付宝 自动发货 php,ecshop支付宝自动发货接口用法示例

    本文实例讲述了ecshop支付宝自动发货接口用法.分享给大家供大家参考,具体如下: 一.在数据库order_info中添加trade_no 字段 SQL语句: 复制代码代码如下: ALTER TABL ...

最新文章

  1. iis7 您无权使用所提供的凭据查看此目录或页面。_使用Spring Cloud和Docker构建微服务架构
  2. 机器学习笔记:Adagrad
  3. 如何精确评估开发时间的 4 个小套路?
  4. [GitHub] 75+的 C# 数据结构和算法实现
  5. python获取手机通知栏消息_Python编写简单的通知栏脚本启动工具
  6. opencv数字图像处理(2) - 直方图处理方法【直方图均衡化与直方图规定化】
  7. 在VC下实现串口通讯
  8. 前端项目:基于Nodejs+vue开发实现酒店管理系统
  9. 【Go】blockchain-tutorial 区块链技术教程
  10. PDMS.NET开发
  11. C++ 相关职位的要求
  12. IMO 2017 T4解答
  13. excel两个指标相关性分析_用Excel做相关性分析方法
  14. 蚂蚁金服 花呗借呗 招聘公告
  15. MIPI换EDP芯片-LT8911EXB芯片,商显行业新选择
  16. 武汉轻工大学计算机学院宿舍,武汉轻工大学有几个校区及校区地址 哪个校区最好...
  17. 穷人跟懒人 富人跟勤快人
  18. Ubuntu 18.04配置及美化 (20.04 / 22.04基本相同)
  19. el-select如何选择整个对象item
  20. 《预言者》:从路人甲到教父的预言之路

热门文章

  1. 涂鸦Wi-FiBLE SoC开发幻彩灯带(6)----幻彩灯带功能演示
  2. (二)Execute
  3. 认知无线电网络架构与协议体系
  4. leetcode 16 -- 3Sum Closest
  5. Java——七种垃圾收集器+JDK11最新ZGC
  6. 测量,信号处理中的精度分析 误差传播理论
  7. 计算机三级网络app,‎App Store 上的“计算机三级网络技术题库”
  8. 有一种温暖,若春风细雨
  9. 点点细雨的项目日记(一)nbsp;需…
  10. android 取消系统默认手机加密(全盘加密)