从linux内核角度谈谈fuse用户态文件系统
网上介绍fuse的文章不少,但是感觉大部分都是在应用层介绍fuse工作原理,对实际调试fuse模块相关问题感觉帮助不大。本文以一个fuse hello例子为切入点,从内核角度谈谈fuse应用层与内核模块的工作细节,同时还介绍fuse内核模块的关键代码。先从 github下载fuse-2.9.8源码,这个版本编译起来很方便,./congfigure、make、make install即可完成编译安装
网上介绍fuse的文章不少,但是感觉大部分都是在应用层介绍fuse工作原理,对实际调试fuse模块相关问题感觉帮助不大。本文以一个fuse hello例子为切入点,从内核角度谈谈fuse应用层与内核层的交互细节,同时还介绍fuse内核模块的关键代码。
先从 github下载fuse-2.9.8源码,这个版本编译起来很方便,./congfigure、make、make install即可完成编译安装fuse。解压后的源码路径在 /mnt/hgfs/share/fuse-2.9.8/。cd example,找到 fuse 源码里经典的 hello demo,简单查看一下源码
[root@localhost example]# cat hello.c
static const char *hello_str = "Hello World!\n";
static const char *hello_path = "/hello";
static int hello_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 = strlen(hello_str);
} else
res = -ENOENT;
return res;
}
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;
filler(buf, ".", NULL, 0);
filler(buf, "..", NULL, 0);
filler(buf, hello_path + 1, NULL, 0);
return 0;
}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;
}
//fuse读只是向传入的buf复制字符串"Hello World!\n",这就是read该 fuse文件系统的文件返回的数据
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,
};
int main(int argc, char *argv[])
{
return fuse_main(argc, argv, &hello_oper, NULL);
}
下边开始该demo涉及的fuse文件系统挂载
[root@localhost example]#mkdir fuse_dir
[root@localhost example]#./hello fuse_dir
之后就会执行fuse库的入口函数fuse_main,创建几个线程,同时将该demo 的fuse文件系统挂载到fuse_dir目录
[root@localhost fuse-2.9.8]#cd fuse_dir
[root@localhost fuse_dir]#ls
[root@localhost fuse_dir]#hello
该文件系统只有一个hello文件
[root@localhost fuse_dir]# cat hello
Hello World!
显然 hello 文件内容是字符串” Hello World!”。本文主要演示cat hello命令与fuse内核模块的交互过程。了解过fuse文件系统的小伙伴,应该知道cat hello实际是调用fuse demo应用层注册struct fuse_operations的read函数hello_read(),这个read函数返回” Hello World!”字符串。所以cat hello最后read的数据是” Hello World!”字符串。strace 跟踪一下
[root@localhost fuse_dir]# strace cat hello
……….
//open fuse 文件系统里的hello文件
open("hello", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0444, st_size=13, ...}) = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
//read "Hello World!\n"字符串
read(3, "Hello World!\n", 65536) = 13
//写入标准输出,之后就会在终端显示出来
write(1, "Hello World!\n", 13Hello World!
) = 13
read(3, "", 65536) = 0
close(3) = 0
…………….
前文提过,挂载fuse hello那个文件系统时,在执行fuse库的fuse_main函数后,创建了几个线程,在读取fuse文件时负责与内核fuse模块的交互。
[root@localhost ~]# ps -lef | grep fuse
1 S root 19903 1 0 80 0 - 58115 futex_ 1月02 ? 00:00:00 /mnt/hgfs/share/fuse-2.9.8/example/.libs/lt-hello fuse_dir
可以看到创建的主进程是PID时19903,进程名字lt-hello。每一个fuse文件系统挂载到某个目录,最后都有一个这样的进程。它是fuse库创建的,当我们读写fuse文件系统里的文件,回调fuse文件系统注册的struct fuse_operations结构的read、write函数,然后把数据传回给读写fuse文件系统里的文件的进程。看下这个进程有哪些线程?
[root@localhost ~]# ls /proc/19903/task/
19903 19904 19905 23583
[root@localhost ~]# cat /proc/19903/task/19904/comm
lt-hello
[root@localhost ~]# cat /proc/19903/task/19905/comm
lt-hello
[root@localhost ~]# cat /proc/19903/task/23583/comm
lt-hello
19903是fuse库创建的主进程,19904、19905、23583是它的三个线程,真正干活的也是这3个线程,线程名字也是lt-hello。主进程19903跟踪过,一直在休眠。19904、19905、23583这三个线程一般情况处于休眠状态,当读写fuse文件时,才会工作起来,这里姑且称为fuse库线程。执行strace跟踪一下
[root@localhost ~]# strace -p 19904
strace: Process 19904 attached
read(3,
[root@localhost ~]# strace -p 19905
strace: Process 19905 attached
read(3,
[root@localhost ~]# strace -p 23583
strace: Process 23583 attached
read(3,
随便找一个查看一下内核栈回溯,都是执行fuse_dev_do_read()->request_wait()后在fc->waitq等待队列头休眠
[root@localhost ~]# cat /proc/19904/stack
[<ffffffffc06a56a4>] fuse_dev_do_read.isra.18+0x274/0x870 [fuse]
[<ffffffffc06a5f7d>] fuse_dev_read+0x7d/0xa0 [fuse]
[<ffffffffa7641b33>] do_sync_read+0x93/0xe0
[<ffffffffa764256f>] vfs_read+0x9f/0x170
[<ffffffffa764342f>] SyS_read+0x7f/0xf0
[<ffffffffa7b7706b>] tracesys+0xa3/0xc9
[<ffffffffffffffff>] 0xffffffffffffffff
实际测试表明,19904、19905、23583这三个fuse库线程,在fuse数据传输环节,只会执行read、writev两个系统调用,平时它们都是执行read系统调用后休眠。另外,本文是演示cat hello命令执行后,fuse内核模块是怎么工作的,这里有牵涉到open、read、writev 等系统调用。如果能跟踪这几个系统调用的内核层层函数执行流程,对我们了解fuse文件系统内核fuse模块的工作细节应该是有帮助的。该怎么跟踪呢?用sytemtap捕捉这些内核函数抓取信息是个好的选择。同时执行下边3个命令,跟踪这3个fuse库线程的系统调用
[root@localhost ~]# strace -p 19904
strace: Process 19904 attached
read(3,
[root@localhost ~]# strace -p 19905
strace: Process 19905 attached
read(3,
[root@localhost ~]# strace -p 23583
strace: Process 23583 attached
read(3,
执行cat hello 触发fuse文件系统读
[root@localhost fuse_dir]# cat hello
Hello World!
strace跟踪3个fuse库线程系统调用打印是
root@localhost ~]# strace -p 19904
strace: Process 19904 attached
read(3, ".\0\0\0\1\0\0\0\337\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 135168) = 46
writev(3, [{"\220\0\0\0\0\0\0\0\337\0\0\0\0\0\0\0", 16}, {"\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0"..., 128}], 2) = 144
read(3, "@\0\0\0\22\0\0\0\342\0\0\0\0\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 135168) = 64
writev(3, [{"\20\0\0\0\0\0\0\0\342\0\0\0\0\0\0\0", 16}], 1) = 16
read(3,
[root@localhost ~]# strace -p 19905
strace: Process 19905 attached
read(3, "0\0\0\0\16\0\0\0\340\0\0\0\0\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 135168) = 48
writev(3, [{" \0\0\0\0\0\0\0\340\0\0\0\0\0\0\0", 16}, {"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16}], 2) = 32
read(3,
[root@localhost ~]# strace -p 23583
strace: Process 23583 attached
read(3, "P\0\0\0\17\0\0\0\341\0\0\0\0\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 135168) = 80
//cat hello后,触发执行该fuse文件系统注册的struct fuse_operations的read函数,这个read函数只是向
//传入的buf复制"Hello World!\n"字符串,然后再执行writev系统调用把这个字符串写入fuse内核模块。
//在fuse内核模块里,把"Hello World!\n"传递给cat hello 进程,这即是cat hello读取的的文件数据。
writev(3, [{"\35\0\0\0\0\0\0\0\341\0\0\0\0\0\0\0", 16}, {"Hello World!\n", 13}], 2) = 29
read(3,
可以发现,这3个fuse库线程最后又执行read函数进入休眠状态,等待下次有fuse读写行为再被唤醒。cat hello命令执行后,这3个fuse库线程以及cat hello进程在fuse内核模块的交互是怎么一个过程呢?用systemtap跟踪这个过程涉及的内核sys_open、sys_read、sys_writev以及fuse相关的函数,来看看整个过程。先根据我的调试结果,把关键流程先列一下:
1 cat hello进程首先执行open系统调用,内核关键流程是:sys_open->do_sys_open->do_filp_open->path_openat->do_last->lookup_fast->fuse_dentry_revalidate ,这个流程主要是open hello文件的前期lookup工作。open一个文件,在内核里肯定需要先vfs层探测一下这个文件是否存在等等。
2 cat hello进程调用fuse_open函数。内核流程是:do_sys_open->do_filp_open->path_openat->do_last->vfs_open->do_dentry_open->fuse_open,这是调用fuse内核模块的open函数。open过程感觉没不是重点,重点在read过程,接着看。
3 cat hello 进程执行read系统系统开始读取数据。这里是重点,执行sys_open-> do_sync_read-> fuse_file_aio_read->generic_file_aio_read->page_cache_sync_readahead->ondemand_readahead->__do_page_cache_readahead->fuse_readpages,调用到fuse内核模块的fuse_readpages函数,正式开始读数据了。在该函数中,首先分配一个struct fuse_req,这里就简称req。req下文多次提到,需记住。
4 接着从fuse_readpages进入read_cache_pages->fuse_readpages_fill函数。在fuse_readpages_fill函数执行fuse_send_readpages函数,然后还设置了req.in、req.out、req->pages[]等成员。req->pages[]需要注意,它应该保存的应该是VFS层传进来将来保存本次read要读取的” Hello World!”字符串的内存page。还有一点需注意,该函数中执行fuse_read_fill函数重点设置了req->in.h.opcode=FUSE_READ,即fuse操作码是读,表示本次是fuse 读。fuse_send_readpages->fuse_request_send->__fuse_request_send函数中,执行queue_request函数,把req加入 fc->pending链表,同时执行wake_up(&fc->waitq)唤醒在fc->waitq等待队列休眠的PID是23583的fuse库线程,线程名字是lt-hello。这个fuse库线程前文介绍过,是主要干活的。之后cat hello 进程进程执行request_wait_answer函数,在req->waitq等待队列休眠。
5 PID是23583 的lt-hello线程被唤醒,重头戏来了,接下来是PID是23583 的lt-hello线程的表演时刻。该lt-hello线程被唤醒前的是执行了read系统调用,函数流程是sys_read->vfs_read->do_sync_read->fuse_dev_read->fuse_dev_do_read->request_wait,就是在request_wait函数中在fc->waitq等待队列休眠的。然后lt-hello线程被cat hello进程唤醒,接着执行fuse_dev_do_read剩余的代码。执行req = list_entry(fc->pending.next, struct fuse_req, list)取出req,这个req就是第4步cat hello进程在fuse_send_readpages函数,放入fc->pending 链表的req。
6 fuse_dev_do_read函数中,lt-hello线程接着执行fuse_copy_one(cs, &in->h, sizeof(in->h))把req->in的数据拷贝到cs->buf,cs->buf被设置指向了iov[0]-> iov_base对应的内存。iov[0]-> iov_base是什么?看下fuse_dev_read函数,就是lt-hello线程read系统调用传入的用户空间buf地址,就形如应用层read(fd,buf,sizeof(buf))这种buf。简单来说,req->in.h.opcode包含了cat hello进程fuse 读操作的操作码,lt-hello线程重点是把这些数据复制到read系统调用的buf。将来lt-hello线程返回用户空间,根据req->in.h.opcode知道了cat hello进程是要读取hello这个fuse文件系统中的文件。在fuse_dev_do_read最后,执行list_move_tail(&req->list, &fc->processing)把req加入fc->processing链表。
7 紧接着,PID是23583 的lt-hello线程调用应用层fuse文件系统注册struct fuse_operations的read函数,就是前文fuse demo hello.c文件的hello_read()函数,复制得到” Hello World!”字符串。然后lt-hello线程执行writev系统调用,把” Hello World!”字符串发送给fuse内核模块。需要强调的一点是这个writev系统调用与普通的write不同,它通过struct iovec结构一次发送多片内存的数据,回过头看下strace –p 23583跟踪该线程执行writev的系统调用情况,如下:
writev(3, [{"\35\0\0\0\0\0\0\0\341\0\0\0\0\0\0\0", 16}, {"Hello World!\n", 13}], 2) = 29
这是一次发送两片buf,第1片buf的数据是"\35\0\0\0\0\0\0\0\341\0\0\0\0\0\0\0",一共16字节数据。第2片buf数据是"Hello World!\n",一共13个字节,这才是cat hello最终想要获取的数据。writev函数内核流程是:sys_writev->vfs_writev->do_readv_writev->do_sync_readv_writev->fuse_dev_write->fuse_dev_do_write。
8 fuse_dev_do_write函数是另一个重点,lt-hello线程首先执行 fuse_copy_one(cs, &oh, sizeof(oh))把本次writev传递的用户空间第1片buf的数据数据复制到struct fuse_out_header oh。接着执行req = request_find(fc, oh.unique)从fc->processing链表取出req,这个req打印证实是第6步执行fuse_dev_do_read函数最后放入fc->processing链表的 req,这个req.page[]保存了cat hello进程read系统调用传入的保存待读取的数据的内存page。接着req->out.h=oh,再执行copy_out_args(cs, &req->out, nbytes),这个函数最终把本次lt-hello线程writev系统调用传递第2片用户空间buf的数据即 "Hello World!\n"复制到req.page[]指定的内存page。就相当于把cat hello进程read系统调用要读取的数据"Hello World!\n"保存的内核VFS层read指定的buf。最后,lt-hello线程执行request_end()函数,唤醒在req->waitq 等待队列上休眠的cat hello进程。
9 cat hello 进程从read系统调用返回,"Hello World!\n"字符串已经被保存到了read系统调用传入的用户空间buf。这个在VFS层read完成,我们重点介绍怎么从fuse内核模块获取"Hello World!\n"就行了,总之终于获取到了要读取的数据。然后cat hello就在终端打印"Hello World!\n"字符串。PID是23583 的lt-hello线程再次执行sys_read->vfs_read->do_sync_read->fuse_dev_read->fuse_dev_do_read->request_wait在fc->waitq等待队列休眠,等待下次cat hello等读取fuse文件系统中的文件再被唤醒。
好了,终于把cat hello读取fuse文件系统中的文件的流程讲完了,挺啰嗦的。这里再简单介绍一下核心点。
- cat hello命令 open hello文件
- cat hello进程执行read系统调用开始读取数据,设置fuse读的指令码,然后把保存本次read数据的内存page设置到req.page[]。接着唤醒PID是23583 的lt-hello线程,lt-hello线程得到fuse读的指令码,lt-hello线程返回应用层。
- lt-hello线程根据fuse读指令码,执行应用层fuse文件系统注册struct fuse_operations的hello_read函数,获取到"Hello World!\n"字符串。
- lt-hello线程执行writev系统调用把"Hello World!\n"字符串发送到fuse内核模块。该系统调用最后执行fuse_dev_do_write函数,把"Hello World!\n"字符串复制到req.page[]指定的内存page。就把"Hello World!\n"字符串复制到了cat hello进程read系统调用VFS层传入的内存,cat hello进程获取到了本次read系统调用要读取的数据。
- cat hello从read系统调用返回应用层,在终端打印"Hello World!\n"字符串。lt-hello线程执行read系统调用进入内核空间后,执行fuse_dev_do_read->request_wait后休眠,等待下次被唤醒。
如果想看更详细的fuse内核模块源码注释,看https://github.com/dongzhiyan-stack/kernel-code-comment。本文到这里结束了,有哪里描述有误的请指出,特别是fuse_dev_do_write函数中执行copy_out_args,向cat hello进程read系统调用传入内存page复制"Hello World!\n"字符串的过程,还有点疑惑。如果哪个小伙伴想看下使用systemtap调试fuse的过程,这里把调试代码和调试过程的打印单独贴下。
[root@localhost ~]# cat fuse.stp #####################fuse模块 read、write、open########### #fuse read probe module("fuse").function("fuse_dev_do_read") { printf("%s %s %d fuse_conn:%p\n",ppfunc(),execname(),tid(),$fc) } probe module("fuse").function("fuse_dev_do_read").return { printf("%s return %s %d reqsize:%d\n",ppfunc(),execname(),tid(),$return) } #fuse write probe module("fuse").function("fuse_dev_do_write") { printf("%s %s %d fuse_conn:%p\n",ppfunc(),execname(),tid(),$fc) } probe module("fuse").function("fuse_dev_do_write").return { printf("%s return %s %d nbytes:%d\n",ppfunc(),execname(),tid(),$return) } #fuse open probe module("fuse").function("fuse_open") { printf("%s %s %d\n",ppfunc(),execname(),tid()) } probe module("fuse").function("fuse_open").return { printf("%s return %s %d\n",ppfunc(),execname(),tid()) } #fuse read page probe module("fuse").function("fuse_readpages") { printf("%s %s %d\n",ppfunc(),execname(),tid()) } probe module("fuse").function("fuse_readpages").return { printf("%s return %s %d\n",ppfunc(),execname(),tid()) } probe module("fuse").function("fuse_readpages_fill") { printf("%s %s %d\n",ppfunc(),execname(),tid()) } probe module("fuse").function("fuse_dev_write") { if(tid() == 23583){#就是这个fuse库线程 lt-hello pid:23583发送最终的文件内容"Hello World!" iov1=$iov+0x10;#iov偏移16字节到iov1 #printf("%s %s %d %s\n",ppfunc(),execname(),tid(),user_string($iov[1]->iov_base))---直接用user_string($iov[1]->iov_base)也可以 printf("%s %s %d iov0_len:%d iov1_string:%s iov1_len:%d\n",ppfunc(),execname(),tid(),$iov->iov_len,user_string(@cast(iov1,"struct iovec")->iov_base),@cast(iov1,"struct iovec")->iov_len) }else{ printf("%s %s %d\n",ppfunc(),execname(),tid()) } } probe module("fuse").function("copy_out_args") { printf("%s %s %d\n",ppfunc(),execname(),tid()) } probe module("fuse").function("__fuse_get_req").return { printf("%s return %s %d req:%p fuse_conn:%p for_background:%d\n",ppfunc(),execname(),tid(),$return,$fc,$for_background) } #probe module("fuse").function("request_find").return #{ # printf("%s return %s %d req:%p fuse_conn:%p\n",ppfunc(),execname(),tid(),$return,$fc) #} #probe kernel.function("fuse_dentry_revalidate") #{ # printf("%s %s %d\n",ppfunc(),execname(),tid()) #} probe module("fuse").function("__fuse_request_send") { printf("%s %s %d fuse_conn:%p req:%p queue_request()、request_wait_answer()\n",ppfunc(),execname(),tid(),$fc,$req) } probe module("fuse").function("queue_request") { printf("%s %s %d fuse_conn:%p req:%p list_add_tail(&req->list, &fc->pending) wake_up(&fc->waitq)\n",ppfunc(),execname(),tid(),$fc,$req) } probe module("fuse").function("fuse_copy_one") { printf("%s %s %d req:%p\n",ppfunc(),execname(),tid(),$cs->req) } probe module("fuse").function("request_end") { printf("%s %s %d req:%p fuse_conn:%p\n",ppfunc(),execname(),tid(),$req,$fc) } ###open、read、write、writev系统调用####################################################### #系统调用 open probe kernel.function("do_sys_open") { if(execname() == "lt-hello" || execname() == "cat") printf("%s %s %d\n",ppfunc(),execname(),tid()) } probe kernel.function("do_sys_open").return { if(execname() == "lt-hello" || execname() == "cat") printf("%s return %s %d\n\n",ppfunc(),execname(),tid()) } #系统调用read probe kernel.function("do_sync_read") { if(execname() == "lt-hello" || execname() == "cat") printf("%s %s %d\n",ppfunc(),execname(),tid()) } probe kernel.function("do_sync_read").return { if(execname() == "lt-hello" || execname() == "cat") printf("%s return %s %d\n\n",ppfunc(),execname(),tid()) } #系统调用write probe kernel.function("do_sync_write") { if(execname() == "lt-hello" || execname() == "cat") printf("%s %s %d write ***%s***\n",ppfunc(),execname(),tid(),user_string($buf)) } probe kernel.function("do_sync_write").return { if(execname() == "lt-hello" || execname() == "cat") printf("%s return %s %d\n\n",ppfunc(),execname(),tid()) } #系统调用 writev probe kernel.function("vfs_writev") { if(execname() == "lt-hello" || execname() == "cat") printf("%s %s %d\n",ppfunc(),execname(),tid()) } probe kernel.function("vfs_writev").return { if(execname() == "lt-hello" || execname() == "cat") printf("%s return %s %d\n\n",ppfunc(),execname(),tid()) } ##########休眠唤醒######################################################## #在fc->waitq 等待队列头休眠 probe kernel.function("add_wait_queue_exclusive") { if(execname() == "lt-hello" || execname() == "cat") printf("request_wait->%s %s %d wait fc->waitq:%p\n",ppfunc(),execname(),tid(),$q) } #在 req->waitq 等待队列头休眠 probe kernel.function("prepare_to_wait") { if(execname() == "lt-hello" || execname() == "cat") printf("%s %s %d wait req->waitq:%p\n",ppfunc(),execname(),tid(),$q) } #唤醒等待队列头q上的进程 probe kernel.function("__wake_up") { if(execname() == "lt-hello" || execname() == "cat") printf("%s %s %d wakeup waitq:%p\n",ppfunc(),execname(),tid(),$q) } #分配 req->waitq 等待队列头 probe kernel.function("__init_waitqueue_head") { if(execname() == "lt-hello" || execname() == "cat") printf("%s %s %d alloc req->waitq:%p\n",ppfunc(),execname(),tid(),$q) } #进程唤醒唤醒 probe kernel.function("try_to_wake_up") { if(kernel_string($p->comm) == "lt-hello" || kernel_string($p->comm) == "cat"){ printf("%s %s %d wake up %s %d\n",ppfunc(),execname(),tid(),kernel_string($p->comm),$p->pid); //print_backtrace(); //printf("\n"); } } //进程休眠 probe kernel.function("__schedule") { if(execname() == "lt-hello" || execname() == "cat"){ printf("%s %s %d\n",ppfunc(),execname(),tid()); //print_backtrace(); //printf("\n"); } }
stap –vg fuse.tap 后cat hello的打印是
//cat open系统调用开始 do_sys_open cat 11724 __init_waitqueue_head cat 11724 alloc req->waitq:0xffff927f161f0bd0 __fuse_get_req return cat 11724 req:0xffff927f161f0af0 fuse_conn:0xffff927f2e8da800 for_background:0 __fuse_request_send cat 11724 fuse_conn:0xffff927f2e8da800 req:0xffff927f161f0af0 queue_request()、request_wait_answer() queue_request cat 11724 fuse_conn:0xffff927f2e8da800 req:0xffff927f161f0af0 list_add_tail(&req->list, &fc->pending) wake_up(&fc->waitq) __wake_up cat 11724 wakeup waitq:0xffff927f2e8da868 try_to_wake_up cat 11724 wake up lt-hello 19904 prepare_to_wait cat 11724 wait req->waitq:0xffff927f161f0bd0 __schedule cat 11724 fuse_copy_one lt-hello 19904 req:0xffff927f161f0af0 fuse_copy_one lt-hello 19904 req:0xffff927f161f0af0 __schedule lt-hello 19904 try_to_wake_up strace 7076 wake up lt-hello 19904 __schedule lt-hello 19904 try_to_wake_up strace 7076 wake up lt-hello 19904 vfs_writev lt-hello 19904 fuse_dev_write lt-hello 19904 fuse_dev_do_write lt-hello 19904 fuse_conn:0xffff927f2e8da800 fuse_copy_one lt-hello 19904 req:0x0 fuse_copy_one lt-hello 19904 req:0xffff927f161f0af0 request_end lt-hello 19904 req:0xffff927f161f0af0 fuse_conn:0xffff927f2e8da800 __wake_up lt-hello 19904 wakeup waitq:0xffff927f161f0bd0 try_to_wake_up lt-hello 19904 wake up cat 11724 fuse_dev_do_write return lt-hello 19904 nbytes:144 vfs_writev return lt-hello 19904 __schedule lt-hello 19904 try_to_wake_up strace 7076 wake up lt-hello 19904 __schedule lt-hello 19904 try_to_wake_up strace 7076 wake up lt-hello 19904 do_sync_read lt-hello 19904 fuse_dev_do_read lt-hello 19904 fuse_conn:0xffff927f2e8da800 request_wait->add_wait_queue_exclusive lt-hello 19904 wait fc->waitq:0xffff927f2e8da868 __schedule lt-hello 19904 prepare_to_wait cat 11724 wait req->waitq:0xffff927f161f0bd0 //cat 调用 fuse_open fuse_open cat 11724 __init_waitqueue_head cat 11724 alloc req->waitq:0xffff927f161f0bd0 __init_waitqueue_head cat 11724 alloc req->waitq:0xffff927f2f8c31d8 __init_waitqueue_head cat 11724 alloc req->waitq:0xffff927f161f0ef0 __fuse_get_req return cat 11724 req:0xffff927f161f0e10 fuse_conn:0xffff927f2e8da800 for_background:0 //-------分配req 0xffff927f161f0e10 __fuse_request_send cat 11724 fuse_conn:0xffff927f2e8da800 req:0xffff927f161f0e10 queue_request()、request_wait_answer() queue_request cat 11724 fuse_conn:0xffff927f2e8da800 req:0xffff927f161f0e10 list_add_tail(&req->list, &fc->pending) wake_up(&fc->waitq) __wake_up cat 11724 wakeup waitq:0xffff927f2e8da868 try_to_wake_up cat 11724 wake up lt-hello 19905 prepare_to_wait cat 11724 wait req->waitq:0xffff927f161f0ef0 __schedule cat 11724 fuse_copy_one lt-hello 19905 req:0xffff927f161f0e10 fuse_copy_one lt-hello 19905 req:0xffff927f161f0e10 __schedule lt-hello 19905 try_to_wake_up strace 7078 wake up lt-hello 19905 __schedule lt-hello 19905 try_to_wake_up strace 7078 wake up lt-hello 19905 vfs_writev lt-hello 19905 fuse_dev_write lt-hello 19905 fuse_dev_do_write lt-hello 19905 fuse_conn:0xffff927f2e8da800 fuse_copy_one lt-hello 19905 req:0x0 fuse_copy_one lt-hello 19905 req:0xffff927f161f0e10 request_end lt-hello 19905 req:0xffff927f161f0e10 fuse_conn:0xffff927f2e8da800 __wake_up lt-hello 19905 wakeup waitq:0xffff927f161f0ef0 try_to_wake_up lt-hello 19905 wake up cat 11724 fuse_dev_do_write return lt-hello 19905 nbytes:32 vfs_writev return lt-hello 19905 __schedule lt-hello 19905 try_to_wake_up strace 7078 wake up lt-hello 19905 __schedule lt-hello 19905 try_to_wake_up strace 7078 wake up lt-hello 19905 do_sync_read lt-hello 19905 fuse_dev_do_read lt-hello 19905 fuse_conn:0xffff927f2e8da800 request_wait->add_wait_queue_exclusive lt-hello 19905 wait fc->waitq:0xffff927f2e8da868 __schedule lt-hello 19905 prepare_to_wait cat 11724 wait req->waitq:0xffff927f161f0ef0 fuse_open return cat 11724 //cat 系统调用open结束 do_sys_open return cat 11724 //cat 系统调用read开始 do_sync_read cat 11724 //cat read调用到 fuse_readpages函数 fuse_readpages cat 11724 __init_waitqueue_head cat 11724 alloc req->waitq:0xffff927f161f0ef0 //分配 req:0xffff927f161f0e10 ,设置 req->in.h.opcode=FUSE_READ 操作码 __fuse_get_req return cat 11724 req:0xffff927f161f0e10 fuse_conn:0xffff927f2e8da800 for_background:1 fuse_readpages_fill cat 11724 //cat进程 send req,把req加入 fc->pending 链表 queue_request cat 11724 fuse_conn:0xffff927f2e8da800 req:0xffff927f161f0e10 list_add_tail(&req->list, &fc->pending) wake_up(&fc->waitq) __wake_up cat 11724 wakeup waitq:0xffff927f2e8da868 //cat 唤醒 lt-hello PID:23583 线程,唤醒前cat 进程在 fuse_dev_do_read 中休眠 try_to_wake_up cat 11724 wake up lt-hello 23583 //cat 进程从 fuse_readpages 返回 fuse_readpages return cat 11724 //cat 进程休眠 __schedule cat 11724 //lt-hello 23583 线程在 fuse_dev_do_read函数中 取出 req:0xffff927f161f0e10,向req.in复制到它read系统调用传入的 //buf中,这里边有fuse read操作码,之后fuse线程根据这个判断本次是fuse read读。 fuse_copy_one lt-hello 23583 req:0xffff927f161f0e10 fuse_copy_one lt-hello 23583 req:0xffff927f161f0e10 __schedule lt-hello 23583 try_to_wake_up strace 7080 wake up lt-hello 23583 __schedule lt-hello 23583 try_to_wake_up strace 7080 wake up lt-hello 23583 //这里边有个问题,没有捕捉到"do_sync_read return lt-hello 23583",测试证明是必须得先do_sync_read //函数执行前运行捕捉命令,才能捕捉到,systemtap的问题 //lt-hello PID:23583 线程执行vfs_writev系统调用开始 vfs_writev lt-hello 23583 //lt-hello PID:23583 线程执行vfs_writev发送"hello World!"字符串 fuse_dev_write lt-hello 23583 iov0_len:16 iov1_string:Hello World! iov1_len:13 fuse_dev_do_write lt-hello 23583 fuse_conn:0xffff927f2e8da800 fuse_copy_one lt-hello 23583 req:0x0 //从 fc->processing 链表 取出 req:0xffff927f161f0e10 req = request_find(fc, oh.unique) //lt-hello PID:23583 writev req:0xffff927f161f0e10 发送字符串 err = copy_out_args(cs, &req->out, nbytes) request_end lt-hello 23583 req:0xffff927f161f0e10 fuse_conn:0xffff927f2e8da800 __wake_up lt-hello 23583 wakeup waitq:0xffff927f161f0ef0 __wake_up lt-hello 23583 wakeup waitq:0xffff927fbffa5bd8 //cat 进程被唤醒,开始读取数据 try_to_wake_up lt-hello 23583 wake up cat 11724 fuse_dev_do_write return lt-hello 23583 nbytes:29 //lt-hello PID:23583 线程执行vfs_writev系统调结束 vfs_writev return lt-hello 23583 __schedule lt-hello 23583 try_to_wake_up strace 7080 wake up lt-hello 23583 __schedule lt-hello 23583 try_to_wake_up strace 7080 wake up lt-hello 23583 do_sync_read lt-hello 23583 //lt-hello 23583 read系统调用后休眠,等待下次fuse 读被唤醒 fuse_dev_do_read lt-hello 23583 fuse_conn:0xffff927f2e8da800 request_wait->add_wait_queue_exclusive lt-hello 23583 wait fc->waitq:0xffff927f2e8da868 __schedule lt-hello 23583 //cat read系统调用结束 do_sync_read return cat 11724 __wake_up cat 11724 wakeup waitq:0xffff927f15c101c0 try_to_wake_up cat 11724 wake up cat 11724 __wake_up cat 11724 wakeup waitq:0xffff927f15c101c0 try_to_wake_up cat 11724 wake up cat 11724 __wake_up cat 11724 wakeup waitq:0xffff927f15c101c0 do_sync_read cat 11724 do_sync_read return cat 11724 __wake_up cat 11724 wakeup waitq:0xffff927f2f8c31d8 queue_request cat 11724 fuse_conn:0xffff927f2e8da800 req:0xffff927f161f0af0 list_add_tail(&req->list, &fc->pending) wake_up(&fc->waitq) __wake_up cat 11724 wakeup waitq:0xffff927f2e8da868 try_to_wake_up cat 11724 wake up lt-hello 19904 fuse_copy_one lt-hello 19904 req:0xffff927f161f0af0 fuse_copy_one lt-hello 19904 req:0xffff927f161f0af0 fuse_dev_do_read return lt-hello 19904 reqsize:64 do_sync_read return lt-hello 19904 __schedule lt-hello 19904 __init_waitqueue_head cat 11724 alloc req->waitq:0xffff927f0f6b3df8 try_to_wake_up strace 7076 wake up lt-hello 19904 __schedule lt-hello 19904 try_to_wake_up strace 7076 wake up lt-hello 19904 vfs_writev lt-hello 19904 fuse_dev_write lt-hello 19904 fuse_dev_do_write lt-hello 19904 fuse_conn:0xffff927f2e8da800 fuse_copy_one lt-hello 19904 req:0x0 request_end lt-hello 19904 req:0xffff927f161f0af0 fuse_conn:0xffff927f2e8da800 __wake_up lt-hello 19904 wakeup waitq:0xffff927f161f0bd0 fuse_dev_do_write return lt-hello 19904 nbytes:16 vfs_writev return lt-hello 19904 __schedule cat 11724 __schedule lt-hello 19904 try_to_wake_up strace 7076 wake up lt-hello 19904 __schedule lt-hello 19904 try_to_wake_up strace 7076 wake up lt-hello 19904 do_sync_read lt-hello 19904 fuse_dev_do_read lt-hello 19904 fuse_conn:0xffff927f2e8da800 request_wait->add_wait_queue_exclusive lt-hello 19904 wait fc->waitq:0xffff927f2e8da868 __schedule lt-hello 19904
更多推荐
所有评论(0)