FUSE使用心得

摘要:
例如一些与文件操作相关的系统调用(例如mkdir,一方面,当使用FUSE开发文件系统时,您可以通过包含此头文件来完成大部分工作。文件系统包含使用FUSE实现的文件系统的一些网站链接;此外,安装包中有一个名为INSTALL的文本文件,并打开此目录(您可以在终端中使用ls命令或图形文件管理工具)例如,hello文件中的内容无法修改(无法保存修改的内容)。

纲要

 

0. 简介

1. FUSE的下载安装参考资料来源

2. 带FUSE的程序的总体分析以及编译方法

3. 遇到的问题解决方案和注意事项

 

 

简介

 

FUSE,,全称Filesystem in Userspace。从名字上看,并不怎么容易理解,其中有一个意义模糊的词Userspace。我以为,此处的User,是相对于kernel而言的。对于一个传统的文件系统,往往是从内核的层面上对一个文件系统进行支持,比如一些和文件操作相关的系统调用(例如mkdiropen等)都是在内核的层次上实现,从而完成对文件的相关操作。一方面,内核态的代码难以调试,开发效率低;另一方面,如果开发一个文件系统就需要程序员对于操作系统的内核了然于胸,导致开发成本进一步提高。而FUSE这个库,把内核关于文件的操作封装起来,转化成一组相对简单的API供开发人员直接使用。这样,程序员不必深入了解操作系统内核就可以开发文件系统了。

 

 

下载安装

 

FUSEsourceforge上的一个开源项目,可以免费的下载源代码并使用FUSE进行二次开发。

sourceforgeFUSE页面(http://fuse.sourceforge.net/ )可以获取到FUSE的源码压缩包。将压缩包解压缩,里面主要有这些东西:doc文件夹,里面有一个kernel.txt,对整个FUSE进行了总体上的介绍;example文件夹,里面包含了几个例子,其中对初学者比较有参考价值的,一个是hello,一个是fusexmp,在下文中会进一步介绍;include文件夹,包含了FUSE的头文件,其中比较重要的是fuse.h,一方面使用FUSE开发文件系统时只要包含这个头文件就可以完成大部分工作,另一方面这个文件中声明了FUSE最核心的API,并且在注释中有一些说明,起到了文档的效果;lib文件夹,包含了FUSE的实现,如果仅仅是使用FUSE开发而不是研究FUSE的源码的话这里就可以略过了;FAQ文件,里面包含了一些常见的问题和解决方案;README里面包含了一些介绍信息;Filesystems包含了一些用FUSE实现的文件系统的网站链接。学习使用FUSE,主要的参考资料便是上面的这些内容。

安装的话,configure + make + make install即可,和在linux上安装一般的程序没什么区别。

FUSE主页上就有关于安装的简要介绍,另外在安装包中有一个名字为INSTALL的文本文件,里面详细的介绍了安装过程,这里不再赘述了。

 

 

如何使用

 

首先,从FUSE提供的例子入手。经过了make之后, FUSE自带的例子都编译出了可执行程序。比如hello这个例子,在fuse/example/目录下新建目录tmp/然后运行 ./hello tmp/ 这样就将这个hello文件系统挂载到tmp/这个目录上了。打开这个目录(在终端中使用ls命令或者使用图形化的文件管理工具都可以),可以看到tmp目录下有一个hello文件,用文本编辑器打开这个文件,可以看到里面的字符串“helloworld”。由于这仅仅是一个最简单的示例性的文件系统,有些基本功能并不完备,比如hello文件中的内容不能被修改(修改了也没有办法保存,因为没有实现相应的操作。实际上,hello这个文件中的内容是在源码中直接写进去的)。如果要尝试更多的操作,可以使用examplefusexmp这个例子。这个例子是把根目录挂载到挂载点上,里面的操作也提供的比较完备。

下面简要分析下hello这个例子的源码。源码在hello.c这个文件中,里面只有不到100C源码。

整体看起来是这样的:最开始处包含了fuse.h这个头文件,然后定义了一些函数。之后定义了一个fuse_operations结构,虽然此处的语法有些晦涩,但是也不难猜出fuse_operations的成员是一些函数指针,然后使用上面实现的函数对这些函数指针进行赋值。最后就是main函数,main函数把所有的工作都交给了fuse_main进行处理了。

那么主要的核心就集中在fuse_operations结构上了。打开fuse.h文件,可以看到fuse_operations是长成这个样子的:里面有很多函数指针。每一个函数指针都对应着一个和文件系统相关的基本操作。程序员为这些接口提供能够操作自己文件系统的具体实现,从而将自己的文件系统和操作系统对接到一起。举个例子,比如在终端中使用ls命令列出目录中的文件,那么如果路径指向的是操作系统自身文件系统中的目录,那么实际上调用的是系统中原有的系统调用readdir;如果路径指向的是程序员自己的文件系统的目录,实际上调用的就是fuse_operations结构中的readdir函数指针指向的函数。每个函数指针的实现规格(输入,输出,注意事项)虽然在注释中有所体现,但是过于简要,很多地方还是需要对操作系统有一定的熟悉程度才会容易理解一些。

总体来看,使用FUSE开发文件系统,是这样的一个过程:定义一个fuse_operations结构,为这个结构的成员提供符合自己需求的实现即可。

 

 

遇到的问题,解决方案和注意事项

 

使用FUSE的程序的编译

通过configure生成的makefile文件,直接make即可将FUSE中所有的example都进行编译。但是如果要单独编译其中的某个例子,或者编译自己写好的文件系统,按照文档的说法是这样的:

 

Gcc -Wall `pkg-config fuse --cflags --libs` hello.c -o hello

 

注意“ ”是键盘左上角,数字1左边的那个按键,而不是单引号。但实际上使用这个命令编译时会报链接错误:“fuse_main_real()函数找不到实现……”之类的。这是一个很典型的链接错误,说明编译器只看到了函数的声明而没有找到这个函数的实现。实际上这个函数的实现在helper.c中,并且被编译到了相应的链接库中。究其原因,其实是gcc的命令行参数传入的顺序问题。`pkg-config fuse --cflags --libs`这个东西,可以把它理解成一个在fuse安装时定义的变量,可以在终端中输入echo `pkg-config fuse --cflags --libs`即可看到这个变量展开之后所表示的gcc的命令行参数,应该是这种形式“-D_FILE_OFFSET_BITS64 -I/usr/includefuse -pthread -lfuse -lrt=”。其中包含了一个-lfuse命令,它的作用是链接fuse相应的链接库,也就是告诉编译器去哪里能找到fuse_main_real()的实现。事实上gcc要求链接库的命令行参数要放在源代码文件之后,所以将上面的命令改成:

 

Gcc -Wall hello.c -o hello `pkg-config fuse --cflags --libs` 

 

即可顺利通过编译。关于gcc的参数说明,具体详情参见gcc的官方文档,此处不再引用。

 

使用g++编译FUSE程序

FUSE除了提供C语言的API,还为多种语言提供了编程接口,比如JavaC#Python等(详见FUSE主页上language binding)。对于C++来说,FUSE也提供了C++风格的API,但实际上由于CC++的兼容性,C++也可以直接使用CAPI,但是需要做出少许的修改。例如像结构体的指定初始化这种C99标准中的语法(也就是hello.cfuse_operation结构体函数指针成员赋值的晦涩语法),g++是不能编译通过的。将其修改成在全局定义一个fuse_operation对象,然后在main函数中对这个fuse_operation对象的成员依次赋值即可。其余的命令行参数都和gcc相同了。

 

FUSE的一个非常有用的命令行参数-d

通过实现一些自定制的函数和FUSE提供的API对接起来从而可以完成整个文件系统的实现。那么怎样知道FUSE的某个API什么时候会被调用呢?或者说实现了一个API之后怎样测试它是否正确?这里就用到了FUSE的一个命令行参数-d。在使用FUSE程序进行挂载的时候,同时使用-d参数,这样在对自己的文件系统进行操作的时候就会在终端中打印一些调试信息。此时最好使用终端通过命令行的方式操作文件系统,如果使用图形化文件管理工具的话,可能一个操作就会打印几十甚至上百条调试信息,分析起来会很麻烦。调试信息中主要包括这样的信息:一个当前操作的序号,操作的名称(虽然和提供的API并不是一一对应,但大部分是和API具有相同名字),输入输出的数据量的大小以及该操作失败时的错误返回码。关于这些调戏信息的具体规格,并没有找到相关的详细说明,所幸大部分信息都不难理解。

 

FUSE程序的调试

关于FUSE的调试确实没有想到什么很好的方法。由于FUSE程序的特殊性质以及自身缺少系统级别的编程经验,没用成功的使用gdbFUSE的代码直接进行跟踪。FUSE的官网上也推荐了一些系统调试工具,由于精力有限也没有一一尝试。一个简洁有效的方法是通过使用cerr打印一些调试信息,并且配合assert进行定位。另一方面,对自己的文件系统的接口部分进行充分的单元测试,这样可以大大降低和FUSE对接之后的调试成本。另外在当FUSE的接口只完成一部分的时候,对这些接口单独测试可能会出现一些很莫名奇妙的bug,比如我就遇到了这样的问题:实现了getattrreaddir命令后,在终端中使用ls命令,打印的文件和目录名有时候会多出一些多余的符号,有时候又会报“IO错误”。幸运的是,当我对FUSE的其他API进一步完善之后,这个问题神奇的自动消失了。好吧,首先要承认带着bug还继续添加代码是一件非常糟糕的事情,但是FUSE的所有API是一个整体,而终端在执行命令的过程中究竟在后面都做了什么事情,我们也不得而知。总之,我要说明的是,如果使用FUSE的过程中出现了一些和预期不符的东西,首先排除它不是由文件系统自身带来的,然后再确定对FUSE的使用方式是否恰当,然后确定测试思路是否正确。

 

 

 

 

 

免责声明:文章转载自《FUSE使用心得》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇使用U盘安装win7系统并在安装过程中使用cmd重新对硬盘分区PythonDay05下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

awk扩展应用

                                                                        awk扩展应用 案例1:使用awk提取文本 案例2:awk处理条件 案例3:awk综合脚本应用 案例4:awk流程控制 案例5:awk扩展应用 1案例1:使用awk提取文本 1.1问题 本案例要求使用awk工具完成...

遇到Visual Studio "当前不会命中断点.还没有为该文档加载任何符号"的情况

一.问题及原因 有这样一种调用逻辑:A.exe调用B.dll.现在想要在B的源代码中打断点,从A发起进行调试,却给出了"当前不会命中断点.还没有为该文档加载任何符号"的提示.感觉十分奇怪,各种重新生成,重启VS都没啥用,最后不得以网上搜了一番,找到了问题的根源. 原来我把旧的B.dll文件拷到了A.exe所在的目录下,导致A.exe调试时直接去调用旧的B....

pcap文件格式及文件解析

第一部分:PCAP包文件格式 一 基本格式:    文件头 数据包头数据报数据包头数据报...... 二、文件头:        文件头结构体 sturct pcap_file_header {      DWORD           magic;      DWORD           version_major;      DWORD       ...

php可选缓存APC

1、APC缓存简介 APC,全称是Alternative PHP Cache,官方翻译叫”可选PHP缓存”。它为我们提供了缓存和优化PHP的中间代码的框架。 APC的缓存分两部分:系统缓存和用户数据缓存。 系统缓存 它是指APC把PHP文件源码的编译结果缓存起来,然后在每次调用时先对比时间标记。如果未过期,则使用缓存的中间代码运行。默认缓存 3600s(...

python2.7中的字符编码问题

转自:https://www.cnblogs.com/liaohuiqiang/p/7247393.html 0. 写在前面 起因:之前写个数据预处理程序的时候遇到了点问题,用re模块的正则查找方法search时总是找不出来(找错了或者出乱码),于是捣鼓捣鼓。 经过:查资料,做实验,发现用utf8编码的str类型的字符串在search方法中行不通,因为st...

Linux之/etc/fstab自动挂载文件讲解

一、/etc/fstab文件的作用磁盘使用mount手动挂载,系统重启后会失效,仍需自己手动挂载。 将磁盘的挂载信息写入/etc/fstab这个文件,就能实现开机自动挂载磁盘,不再需要自己手动挂载了。 二、挂载的限制     在说明这个文件的作用之前我想先强调一下挂载的限制。   1、根目录是必须挂载的,而且一定要先于其他mount point被挂载。因为...