转载自:http://blog.chinaunix.net/uid-8350672-id-2031702.html
一、FUSE简介
FUSE(用户空间文件系统)是这样一个框架,它使得FUSE用户在用户态下编写文件系统成为可能,而不必和内核打交道。FUSE由三个部分组成,linux内核模块、FUSE库以及mount 工具。用户关心的只是FUSE库和mount工具,内核模块仅仅提供kernel的接入口,给了文件系统一个框架,而文件系统本身的主要实现代码位于用户空间中。 FUSE库给用户提供了编程的接口,而mount工具则用于挂在用户编写的文件系统。
FUSE起初是为了研究AVFS(A Virtual Filesystem)而设计的,而现在已经成为SourceForge的一个独立项目,目前适用的平台有Linux, FreeBSD, NetBSD, OpenSolaris和Mac OS X。官方的linux kernel版本到2.6.14才添加了FUSE模块,因此2.4的内核模块下,用户如果要在FUSE中创建一个文件系统,需要先安装一个FUSE内核模块,然后使用FUSE库和API来创建。
二、FUSE特性
? 库文件和 API简单,极大地方便了用户的使用
? 安装简便,不需要加补丁或者重新编译 kernel
? 执行安全,使用稳定
? 高效,相对于其它用户态文件系统实例
? 非特权用户可以使用
? 基于 linux2.4.x 和 2.6.x 内核,现在可以支持JavaTM 绑定,不必限定使用C和C++来编 写文件系统
三、源代码目录
? ./doc 包含FUSE相关文档
? ./include 包含了FUSE API头,对创建文件系统有用,主要用fuse.h
? ./lib 存放FUSE库的源代码
? ./util 包含了FUSE工具库的源代码
? ./example 参考的例子
四、安装
FUSE的源码安装类似于其他软件,只需要在FUSE的源码目录下执行如下命令即可:
1
2
3
|
.
/configure
make
make
install
(以root身份执行)
|
五、FUSE operations
FUSE使用fuse_operations来给用户提供编程结构,让用户通过注册自己编写的函数到该结构体来实现自己的文件系统。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
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,
struct
fuse_file_info *);
int
(*write) (
const
char
*,
const
char
*,
size_t
, off_t,
struct
fuse_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,
struct
flock *);
int
(*utimens) (
const
char
*,
const
struct
timespec tv[2]);
int
(*bmap) (
const
char
*,
size_t
blocksize, uint64_t *idx);
};
|
六、hello示例文件系统源码分析
FUSE在源码目录example下有一些示例文件系统,通过阅读这些示例文件系统可以掌握FUSE用户态文件系统的编写规范。下面以hello.c为例分析FUSE的编写规范:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
#define FUSE_USE_VERSION 26
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
static
const
char
*hello_str =
"Hello World!\n"
;
static
const
char
*hello_path =
"/hello"
;
/*该函数与stat()类似,用于得到文件的属性,将其存入到结构体struct stat中*/
static
int
hello_getattr(
const
char
*path,
struct
stat *stbuf)
{
int
res = 0;
memset
(stbuf, 0,
sizeof
(
struct
stat));
//用于初始化结构体stat
if
(
strcmp
(path,
"/"
) == 0) {
stbuf->st_mode = S_IFDIR | 0755;
//S_IFDIR 用于说明/为目录,详见S_IFDIR定义
stbuf->st_nlink = 2;
//文件链接数
}
else
if
(
strcmp
(path, hello_path) == 0) {
stbuf->st_mode = S_IFREG | 0444;
//S_IFREG用于说明/hello为常规文件
stbuf->st_nlink = 1;
stbuf->st_size =
strlen
(hello_str);
//设置文件长度为hello_str的长度
}
else
res = -ENOENT;
//返回错误信息,没有该文件或目录
return
res;
//执行成功返回0
}
/*该函数用于读取/目录中的内容,并在/目录下增加了. .. hello三个目录项*/
static
int
hello_readdir(
const
char
*path,
void
*buf, fuse_fill_dir_t filler,
off_t offset,
struct
fuse_file_info *fi)
{
(
void
) offset;
(
void
) fi;
if
(
strcmp
(path,
"/"
) != 0)
return
-ENOENT;
/*
fill的定义:
typedef int (*fuse_fill_dir_t) (void *buf, const char *name,
const struct stat *stbuf, off_t off);
其作用是在readdir函数中增加一个目录项
*/
filler(buf,
"."
, NULL, 0);
//在/目录下增加. 这个目录项
filler(buf,
".."
, NULL, 0);
// 增加.. 目录项
filler(buf, hello_path + 1, NULL, 0);
//增加hello目录项
return
0;
}
/*用于打开hello文件*/
static
int
hello_open(
const
char
*path,
struct
fuse_file_info *fi)
{
if
(
strcmp
(path, hello_path) != 0)
return
-ENOENT;
if
((fi->flags & 3) != O_RDONLY)
return
-EACCES;
return
0;
}
/*读取hello文件时的操作,它实际上读取的是字符串hello_str的内容*/
static
int
hello_read(
const
char
*path,
char
*buf,
size_t
size, off_t offset,
struct
fuse_file_info *fi)
{
size_t
len;
(
void
) fi;
if
(
strcmp
(path, hello_path) != 0)
return
-ENOENT;
len =
strlen
(hello_str);
if
(offset < len) {
if
(offset + size > len)
size = len - offset;
memcpy
(buf, hello_str + offset, size);
}
else
size = 0;
return
size;
}
/*注册上面定义的函数*/
static
struct
fuse_operations hello_oper = {
.getattr = hello_getattr,
.readdir = hello_readdir,
.open = hello_open,
.read = hello_read,
};
/*用户只需要调用fuse_main(),剩下的事就交给FUSE了*/
int
main(
int
argc,
char
*argv[])
{
return
fuse_main(argc, argv, &hello_oper, NULL);
}
|
终端运行:
1
2
3
|
~
/fuse/example
$
mkdir
/tmp/fuse
//
在
/tmp
下建立fuse目录,用于挂载hello文件系统
~
/fuse/example
$ .
/hello
/tmp/fuse
//
挂载hello文件系统
~
/fuse/example
$
ls
-l
/tmp/fuse
//
执行
ls
时,会调用到readdir函数,该函数会添加一个hello 文件
|
1
2
|
总用量 0
-r--r--r-- 1 root root 13 1970-01-01 07:00 hello
|
1
2
|
~
/fuse/example
$
cat
/tmp/fuse/hello
//
执行
cat
hello时,会调用
open
以及
read
函数,将
Hello World! 字符串hello_str中的内容读出
|
1
|
~
/fuse/example
$ fusermount -u
/tmp/fuse
//
卸载hello文件系统
|
通过上述的分析可以知道,使用FUSE必须要自己实现对文件或目录的操作,系统调用也会最终调用到用户自己实现的函数。用户实现的函数需要在结构体fuse_operations中注册。而在main()函数中,用户只需要调用fuse_main()函数就可以了,剩下的复杂工作可以交给FUSE。
wiki::FUSE::
project::FUSE::
本文转自UltraSQL51CTO博客,原文链接: http://blog.51cto.com/ultrasql/1641974,如需转载请自行联系原作者