Chinaunix首页 | 论坛 | 博客
  • 博客访问: 200646
  • 博文数量: 55
  • 博客积分: 2330
  • 博客等级: 大尉
  • 技术积分: 504
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-20 22:55
文章分类

全部博文(55)

文章存档

2013年(6)

2012年(7)

2011年(7)

2010年(35)

我的朋友

分类: 服务器与存储

2010-08-16 21:58:20

上一篇说明了fuse的并行性,也就是同个回调函数是可以重入的。
这一篇主要是研究了fuse的read操作的并行性,说明fuse默认有可能为应用层的一个I/O请求提供并发性。

问题:
在前面的实验中,fuse中除了read操作外,所有函数都使用全局的CLIENT cl句柄来连接mds和osd(当然,与各个机器之间的CLIENT是独立),这样的好处是,客户端不必为每个i/o请求都创建一个CLIENT,以致服务器端或osd端的portmap(用于rpc程序端口的查询)过于繁忙。

如果read中也使用全局的CLIENT句柄,则会出现很奇怪的错误。
发现,这些错误其实都是由于fuse的read操作有并发造成的。

实验1:
在read操作中使用全局的CLIENT句柄,程序还插入了一些终端输出和中断(就是一处getchar())便于调试
比如这样子:
static int dnfs_read(const char *path, char *buf, size_t size, off_t offset,
                    struct fuse_file_info *fi)
{
        printf("[dnfs_read]: %s, size = %u, offset = %ld\n", path, size, offset);
        static int flag = 0;

        flag++;
        printf(">>>>>>>>>>flag = %d<<<<<<<<<<<<<\n", flag);
        getchar();
        ......

}

1$ ./client  -d /mnt/dnfs/
2$ cd /mnt/dnfs && cp 1m ~/
1号终端启动了文件系统后台,2号终端进入文件系统dnfs的挂载点(根目录),再把该文件系统下的“1m“文件拷贝到用户home目录

下面是2$输出的log片段:

unique: 8, opcode: READ (15), nodeid: 3, insize: 80
READ[0] 16384 bytes from 0
[dnfs_read]: /77/1m, size = 16384, offset = 0
>>>>>>>>>>flag = 1, press any key here<<<<<<<<<<<<<   //插入的中断,这里全部拍回车,所以下面这一空行
 
co_read failed, flag = 1
192.168.0.70: RPC: Remote system error  //连接不上osd70,不过没关系,后面会重连的
destroy clo: 0x6490e0
return -5
   unique: 8, error: -(Input/output error), outsize: 16
unique: 9, opcode: READ (15), nodeid: 3, insize: 80
READ[0] 4096 bytes from 0
[dnfs_read]: /77/1m, size = 4096, offset = 0
>>>>>>>>>>flag = 2, press any key here<<<<<<<<<<<<<

return 4096
   READ[0] 4096 bytes
   unique: 9, error: 0 (Success), outsize: 4112       //这次OK了
unique: 10, opcode: READ (15), nodeid: 3, insize: 80
READ[0] 32768 bytes from 16384
[dnfs_read]: /77/1m, size = 32768, offset = 16384
>>>>>>>>>>flag = 3, press any key here<<<<<<<<<<<<<  
//问题出现了,可以看到,这里并没有输入回车(或任何键),下一个请求(unique 11)就到了,显然这里出现了并发
unique: 11, opcode: READ (15), nodeid: 3, insize: 80
READ[0] 4096 bytes from 4096
[dnfs_read]: /77/1m, size = 4096, offset = 4096
>>>>>>>>>>flag = 4, press any key here<<<<<<<<<<<<< //这里输入的回车对应的是flag=3,也就是unique 10的请求

co_read failed, flag = 4
192.168.0.70: RPC: Remote system error
destroy clo: 0x2aaaac009990
return -5
   unique: 10, error: -(Input/output error), outsize: 16 //unique 10请求到此返回。下面的回车对应的是unique 11号请求的中断

co_read 0 : res = 4096
return 4096
   READ[0] 4096 bytes
   unique: 11, error: 0 (Success), outsize: 4112

上面的例子说明了read中的并发。但这个例子是可以顺利通过的,只要不断的拍回车,最后的结果也是被验证是正确的。其实之所以是正确的,是因为在flag=2之后(也就是flag=3那里的输出)虽然并发产生了unique 10和11两个请求,但是由于我们设置了中断,所以unique11其实一直被拖到unique10执行完之后才真的的执行,也就是说unique10的执行并不妨碍到unique11的执行。

下面实验2把实验1中的中断(getchar()那行)去掉:

unique: 7, opcode: OPEN (14), nodeid: 3, insize: 48
OPEN[0] flags: 0x8000 /77/1m
unique: 8, opcode: READ (15), nodeid: 3, insize: 80
READ[0] 16384 bytes from 0
[dnfs_read]: /77/1m, size = 16384, offset = 0
>>>>>>>>>>flag = 1<<<<<<<<<<<<<
co_read failed, flag = 1
192.168.0.70: RPC: Remote system error
destroy clo: 0x6480e0
return -5
   unique: 8, error: -(Input/output error), outsize: 16
unique: 9, opcode: READ (15), nodeid: 3, insize: 80
READ[0] 4096 bytes from 0
[dnfs_read]: /77/1m, size = 4096, offset = 0
>>>>>>>>>>flag = 2<<<<<<<<<<<<<
co_read 0 : res = 4096
return 4096
   READ[0] 4096 bytes
   unique: 9, error: 0 (Success), outsize: 4112
unique: 10, opcode: READ (15), nodeid: 3, insize: 80
READ[0] 32768 bytes from 16384  //看到读32768字节的是unique10
[dnfs_read]: /77/1m, size = 32768, offset = 16384
>>>>>>>>>>flag = 3<<<<<<<<<<<<<           //同实验1中一样,这里产生了并发unique10和11
unique: 11, opcode: READ (15), nodeid: 3, insize: 80
READ[0] 4096 bytes from 4096    //要读4096字节的是unique11
[dnfs_read]: /77/1m, size = 4096, offset = 4096
>>>>>>>>>>flag = 4<<<<<<<<<<<<<
co_read 0 : res = 4096
return 4096               //注意,这里读返回的是4096,所以对应的是unique11请求
   READ[0] 4096 bytes      //返回值也是对的,读了(返回)4096
   unique: 10, error: 0 (Success), outsize: 4112 //但是却被当成unique10返回了!!!!!!
<cs_read>flag = 4: read from server failed : 192.168.0.2: RPC: Timed out     //接着这里出现了奇怪的flag = 4,不要以为这是unique11的输出(unique11已经在前面被当成unique10返回了),这里是unique10的输出

return -3
   unique: 11, error: -(No such process), outsize: 16//结果unique10的执行结果被当成unique11执行结果返回了!!!!!
unique: 12, opcode: FLUSH (25), nodeid: 3, insize: 64
FLUSH[0]
   unique: 12, error: -38 (Function not implemented), outsize: 16
unique: 13, opcode: RELEASE (18), nodeid: 3, insize: 64
RELEASE[0] flags: 0x8000
[dnfs_release]
   unique: 13, error: 0 (Success), outsize: 16

显然,这一次的结果是失败的。unique10和11并发产生,由于竞争的结果,unique11比unique10先从mds端得到返回结果,但却被fuse误以为这是unique10的返回值;同样,接下来当unique10返回时,结果也被当成unique11的结果了。

这样关于fuse的read为什么不能使用全局的CLIENT句柄,原因就很清楚了:由于fuse为上层的I/O请求产生了并发,如果使用一个全局的CLIENT句柄,则会产生竞争,致使结果错乱。


最后一点猜测:
前面的测试都是在一个客户端机器上只有一个进程在访问,如果有多个进程同时访问,比如写操作,这时fuse也会产生并发,那写操作如果使用全局的CLIENT句柄,那结果也很有可能是错的,跟上面的读操作类似。
这有待后面实验的验证。

-----
后面实验证明,如果在fuse中所有的fop操作共用一个CLIENT会造成问题的,如果这时客户端上同时运行多个进程,就会出现类似上面的数据混乱。
所有,结论是fuse中每一个fop操作都是可重入的,要确保资源共享与互斥。

正如上一篇fuse是并发处理I/O请求的最后所说的,如果要取消fuse的并发性,可以在启动fuse时加入-s参数。


阅读(1030) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~