文章包括如下几个部分:(1)linux2.6.32内核编译过程,(2)pvfs2.8内核模块编译,(3)pvfs2.8集群安装,(4)pvfs2.8编程使用。实验环境为ubuntu10.4,其内核版本为linux2.6.32-21。
pvfs2.8源代码下载:www.pvfs.org
linux2.6版本源代码下载:http://www.kernel.org/pub/linux/kernel/v2.6/
pvfs2.8在客户端为了使用VFS的接口,需要安装其内核支持模块。注意下面的操作都是root权限。
(1)linux2.6.32编译和重启新内核:
看到说明文档中有这么一段话:
所以我决定首先重新编译内核,ubuntu下默认是没有linux内核源代码的,所以下载之。编译步骤如下,首先下载源代码解压至/usr/src处:
root@luoxiongwei-vm:/usr/src#ln -s linux-2.6.32.21 linux2.6
root@luoxiongwei-vm:/usr/src#cd linux2.6
root@luoxiongwei-vm:/usr/src#make oldconfig
#源代码的ReadMe手册中对makeoldconfig有如下解释:
# Default all questions based on the contentsof your existing ./.config file and asking
#about new config symbols。其实这样我们就免去了繁琐的内核配置过程,一切照旧即可。
root@luoxiongwei-vm:/usr/src/linux2.6#make -j 4 #我的是多核机器,开启多线程编译
root@luoxiongwei-vm:/usr/src/linux2.6#make modules_install install
root@luoxiongwei-vm:/usr/src/linux2.6# mkinitramfs -o /boot/initrd.img.2.6.32.21 /lib/modules/2.6.32.21
至此内核编译安装完毕,接下来就是启用新内核。
如果ubuntu10.4没有安装grub那就安装一下。
root@luoxiongwei-vm:~# apt-get install grub
root@luoxiongwei-vm:~# update-grub
执行上述命令,grub会自动寻找系统中的可用内核,并生成启动文件menu.lst
红色框住的就是我们刚刚编译出来的新内核。很遗憾的是ubuntu10.4中grub并不使用menu.lst
作为启动文件。真正的配置文件是/boot/grub/grub.cfg。手动编辑这个配置文件。发现文件头提示:
不管他,强制获取写权限之后,编辑之。
大红色框为我新建的启动项,放到所有启动项的最前面。下面的小红色框是ubuntu10.4原来的第一个启动项。新建启动项格式仿照旧格式很容易编写,所需要的信息在menu.lst可以全部找到。此时重新启动系统就会启用新的内核。
至此启用新内核完毕。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------(2)pvfs2.8内核模块编译:
该过程会编译出所有的pvfs2.8可执行文件,工具程序,客户端内核模块,以及各种配置文件,并安装相应头文件为编写程序做准备。在编译之前请参见前面的文章安装好相应的库,否则会配置出错http://blog.csdn.net/luo6620378xu/article/details/8641856。步骤如下:
将pvfs2.8源代码下载解压至/usr/src处。
root@luoxiongwei-vm:/usr/src# ln -s pvfs-2.8.2 pvfs2
root@luoxiongwei-vm:/usr/src# cd pvfs2/
root@luoxiongwei-vm:/usr/src/pvfs2#./configure --with-kernel=/usr/src/linux2.6
配置结果为:
红色框表明我们会生成pvfs2.ko内核模块,加载该模块就可以使用现成的VFS工具和编程结果来操作pvfs2.8。
root@luoxiongwei-vm:/usr/src/pvfs2# make #编译可执行文件
root@luoxiongwei-vm:/usr/src/pvfs2# make install
root@luoxiongwei-vm:/usr/src/pvfs2# make kmod #编译.ko内核模块
root@luoxiongwei-vm:/usr/src/pvfs2# make kmod_install
编译出来的pvfs2.ko位于/usr/src/pvfs2/src/kernel/linux-2.6/pvfs2.ko待会我们会用带该文件。
这一部分完成之后,可以参照http://blog.csdn.net/luo6620378xu/article/details/8641856这篇文章配置单机版的pvfs2.8,其实集群配置和单机版大同小异。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
(3)pvfs2.8集群安装:
pvfs2.8与pvfs1.6有一个明显的区别。pvfs1.6中有明显的三个可执行文件pvfsd,iod,mgr分别来代表客户端,存储节点以及元数据服务器。而pvfs2.8中只有两个可执行文件pvfs2-server,pvfs2-client。pvfs2-server会根据配置文件自动识别是该作为元数据服务器还是存储节点。pvfs2.8对pvfs1.6完全重写了,采用了分布式的元数据服务器机制,以及多线程,异步I/O等技术。我以前阅读的是pvfs1.6的源代码,写得比较恶心。不过为了要毕业还是硬着头皮看完了。下面给出pvfs2.8集群安装过程,pvfs1.6估计没有什么人用了。1.6的集群安装也是比较简单的,而且要支持VFS现有程序集,蛋疼的只能跑在linux2.4.XXX内核上面。
3.1在各台机器上面安装http://blog.csdn.net/luo6620378xu/article/details/8641856提到的三个库。
3.2编辑各台机器上的/etc/hosts文件,我的集群例子如下。用的是真实的独立IP地址,不要人肉我啊。其中的机器名你得保证跟hostname命令的输出是一样的。
集群为1台客户机:125.216.243.38 luoxiongwei-vm
1一台元数据服务器:125.216.243.15 luoxiongwei-G41M-ES2H
4台存储节点:
125.216.243.22 luoxiongwei
125.216.243.21 yeliqian
125.216.243.138 zhaolinlin
125.216.243.35 datanode2
3.3在元数据服务器上执行如下命令:
pvfs2-genconfig/etc/pvfs2-fs.conf
pvfs2-genconfig是一个交互式的脚本,根据提示输入相应配置信息即可。产生的配置文件为/etc/pvfs2-fs.conf
3.4在元数据服务器处启动pvfs2-server
pvfs2-server /etc/pvfs2-fs.conf -f # -f选项会创建/pvfs2-storage-space目录之后abort掉进程。在次执行
pvfs2-server /etc/pvfs2-fs.conf #元数据服务器就起来的。
3.5接下就是无聊的工作了。
将pvfs2-fs.conf拷贝到四台存储节点的/etc目录下面。
pvfs2-server也拷贝到/usr/local/bin目录下面。
然后依次执行pvfs2-server/etc/pvfs2-fs.conf -f以及pvfs2-server /etc/pvfs2-fs.conf 。至此所有的存储节点起来了。
3.6安装客户端步骤:
建立挂载点:mkdir/mnt/pvfs2
编辑配置文件/etc/pvfs2tab。我的内容如下:
tcp://luoxiongwei-G41M-ES2H:3334/pvfs2-fs /mnt/pvfs2 pvfs2defaults,noauto 0 0
其中的luoxiongwei-G41M-ES2H是我的元数据服务器。系统中其实是可以有多台元数据服务器的。接下来的步骤就参考我写的脚本吧。
红色框上面的是真正要执行的命令。分别是将pvfs2.ko加载入内核,启动client端后台进程,挂载文件系统。
红色框是测试pvfs2.8集群是否正常运行。
至此整个集群安装和配置完毕。要重启或者删除整个系统也很简单。
在各存储节点和元数据服务器killall掉服务进程,rm -rf /pvfs2-storage-space目录。在客户端执行
umount/mnt/pvfs2
killallpvfs2-client
rmmod pvfs2
搞定。想要重启那就重新执行以上各个步骤,是不是很无聊,确实很无聊。建议全部写成脚本。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
(4)pvfs2.8编程使用
一旦在客户端安装了pvfs2.ko,则一切VFS工具和编程接口都可以使用,很爽吧。
只是在使用creat,open等函数编程时在文件名前面别忘了加上/mnt/pvfs2这个挂载目录作为前缀。
VFS工具使用,直接用即可:
time cp call0-1358908306.log /mnt/pvfs2/
下面贴出一个文件,我实验用的测试代码作为例子。
#include <iostream>
#include "spfs.h"
#include <cstring>
#include <new>
#include <ctime>
#include <cstdio>
#include <string>
#include <cstdlib>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "spfs_file.h"
#include "spfs_gettime.h"
#include "record_time.h"
using namespace std;
//小文件比较延时,大文件比较访问速度。
//
void usage()
{
cout<<"This program test big/small file write of pvfs and spfs.Please run as root."<<endl;
cout<<"You should test -w -a -r as seqence."<<endl;
cout<<"-s test spfs,-p test pvfs."<<endl;
cout<<"-b for bigfile as for spfs run as block,-k for smallfile as for spfs run as kv."<<endl;
cout<<"-w upload all file,-a append write file,-r random write file,size to random writet."<<endl;
cout<<"as for write MB for bigfile,KB for smallfile."<<endl;
cout<<"-m iods numbers"<<endl;
cout<<"usage:./writefile [-s|-p] [-b|-k] [-f filename] [-w size|-a size|-r size] [-m iods]"<<endl;
cout<<"eg:./writefile -s -b -f data -w 8 -m 2"<<endl;
cout<<"which means test spfs bigfile upload data 8MB to 2 iods."<<endl;
}
int produce_data(string &filename,int size,int file)
{
int sz;
if(file==1)
sz=size*1024*1024;
if(file==0)
sz=size*1024;
char *p=new (nothrow) char[sz];
if(p==NULL)
{
perror("new err.");
return -1;
}
for(size_t i=0;i!=sz;++i)
p[i]='l';
int op=open(filename.c_str(),O_WRONLY|O_APPEND|O_CREAT,S_IRWXU|S_IRWXG);
if(op==-1)
{
perror("open err.");
delete [] p;
return -1;
}
write_pack(op,p,sz);
close(op);
delete [] p;
return 0;
}
int main(int argc,char **argv)
{
if(argc<9)
{
usage();
exit(-1);
}
extern spfs_fdesc fds[];
extern char * optarg;
ssize_t result;
int num=1;
int iod_num;
short flag;//1:spfs,2:pvfs
short type;//1:upload all file,2:append write,3:random write
short file;//1:block for bigfile -b,0:kv for smallfile -k
int size;
CycleTimer tm;
string file_name;
string record_name;
while((result=getopt(argc,argv,"spbkf:w:a:r:m:"))!=-1)
{
switch(result)
{
case 's':
flag=1;
break;
case 'p':
flag=2;
break;
case 'b':
file=1;
break;
case 'k':
file=0;
break;
case 'f':
file_name.clear();
file_name.append(optarg);
break;
case 'w':
type=1;
size=atoi(optarg);
break;
case 'a':
type=2;
size=atoi(optarg);
break;
case 'r':
size=atoi(optarg);
type=3;
break;
case 'm':
iod_num=atoi(optarg);
break;
default:
cout<<"err."<<endl;
break;
}
}
if(flag==1)//spfs
{
switch(type)
{
case 1://upload whole file
{
//produce data
if(produce_data(file_name,size,file)==-1)
{
perror("produce data err.");
exit(-1);
}
start_lib();
int cr_r=spfs_create(file_name.c_str(),666,file);
if(cr_r==-1)
{
perror("in upload whole spfs_create err.");
end_lib();
exit(-1);
}
int open_fd=spfs_open(file_name.c_str(),O_WRONLY);
if(open_fd==-1)
{
perror("in upload whole spfs_open err.");
end_lib();
exit(-1);
}
char *ptr=new char;
tm.Start();
spfs_write(open_fd,ptr,0,0);
tm.Stop();
int ret_cl=spfs_close(open_fd);
if(ret_cl==-1)
{
perror("in upload spfs_close err.");
end_lib();
exit(-1);
}
end_lib();
record_name.clear();
string nm;
if(file==1)//bigfile
{
nm.append("bigfile");
}
if(file==0)//smallfile
{
nm.append("smallfile");
}
char iod[8]={'\0'};
snprintf(iod,sizeof(iod),"%02d",iod_num);
record_name.append("spfs_write_");
record_name.append(nm);
record_name.append("_upload_");
string ty;
if(file==1)
ty.append("MB");
if(file==0)
ty.append("KB");
char s[8]={'\0'};
snprintf(s,sizeof(s),"%02d",size);
record_name.append(s);
record_name.append(ty+"_");
record_name.append(iod);
record_name.append("iods_");
if(write_time(record_name,num,tm.Get())<0)
{
perror("in spfs write upload record time err.");
exit(-1);
}
exit(0);
}
break;
case 2://append write
{
//just use append filename to append write to the exited files.
//produce data
if(produce_data(file_name,size,file)==-1)
{
perror("produce data err.");
exit(-1);
}
string app_data;
File::ReadFileToString(file_name.c_str(),&app_data);
if(app_data.size()!=0)//sucessfully get data
{
start_lib();
int op_fd=spfs_open(file_name.c_str(),O_APPEND|O_WRONLY);
if(op_fd==-1)
{
perror("in spfs append write spfs_open err.");
end_lib();
exit(-1);
}
int ret;
tm.Start();
ret=spfs_write(op_fd,(void *)const_cast<char *>(app_data.c_str()),app_data.size(),fds[op_fd].fd.meta.u_stat.st_size);
tm.Stop();
int cl_ret=spfs_close(op_fd);
if(cl_ret==-1)
{
perror("in spfs append spfs_close err.");
end_lib();
exit(-1);
}
end_lib();
if(file==1)
cout<<"append ret="<<ret/(1024*1024)<<"MB."<<endl;
if(file==0)
cout<<"append ret="<<ret/1024<<"KB."<<endl;
record_name.clear();
string nm;
if(file==1)//bigfile
{
nm.append("bigfile");
}
if(file==0)//smallfile
{
nm.append("smallfile");
}
char iod[8]={'\0'};
snprintf(iod,sizeof(iod),"%02d",iod_num);
record_name.append("spfs_write_");
record_name.append(nm);
record_name.append("_append_");
string ty;
if(file==1)
ty.append("MB");
if(file==0)
ty.append("KB");
char s[8]={'\0'};
snprintf(s,sizeof(s),"%02d",size);
record_name.append(s);
record_name.append(ty+"_");
record_name.append(iod);
record_name.append("iods_");
if(write_time(record_name,num,tm.Get())<0)
{
perror("in spfs write append record time err.");
exit(-1);
}
exit(0);
}
else
{
perror("in spfs append get data to append err.");
exit(-1);
}
}
break;
case 3://random write
{
//produce data
if(produce_data(file_name,size,file)==-1)
{
perror("produce data err.");
exit(-1);
}
string app_data;
File::ReadFileToString(file_name.c_str(),&app_data);
if(app_data.size()!=0)
{
start_lib();
int op_fd=spfs_open(file_name.c_str(),O_WRONLY);
if(op_fd==-1)
{
perror("in spfs random write spfs_open err. ");
end_lib();
exit(-1);
}
int ret;
size_t offset;
size_t len;
srand(time(NULL));
size_t file_size=fds[op_fd].fd.meta.u_stat.st_size;
if(file==1)//bigfile
{
file_size=(file_size/(1024*1024));
cout<<"file_size="<<file_size<<"MB."<<endl;
}
if(file==0)//smallfile
{
file_size=file_size/1024;
cout<<"file_size="<<file_size<<"KB."<<endl;
}
size_t un=rand()%file_size;
if(file==1)//bigfile
{
un=un*1024;
}
offset=(un*1024);
if(file==1)
cout<<"spfs the random start offset="<<offset/(1024*1024)<<"MB"<<endl;
if(file==0)
cout<<"spfs the random start offset="<<offset/1024<<"KB"<<endl;
tm.Start();
ret=spfs_write(op_fd,(void *)const_cast<char *>(app_data.c_str()),app_data.size(),offset);
tm.Stop();
int cl_re=spfs_close(op_fd);
if(cl_re==-1)
{
perror("in spfs randrom write spfs_close err.");
end_lib();
exit(-1);
}
end_lib();
if(file==1)
cout<<"random write="<<ret/(1024*1024)<<"MB."<<endl;
if(file==0)
cout<<"random write="<<ret/1024<<"KB."<<endl;
//record time
record_name.clear();
string nm;
if(file==1)//bigfile
{
nm.append("bigfile");
}
if(file==0)//smallfile
{
nm.append("smallfile");
}
char iod[8]={'\0'};
snprintf(iod,sizeof(iod),"%02d",iod_num);
record_name.append("spfs_write_");
record_name.append(nm);
record_name.append("_random_");
string ty;
if(file==1)
ty.append("MB");
if(file==0)
ty.append("KB");
char s[8]={'\0'};
snprintf(s,sizeof(s),"%02d",size);
record_name.append(s);
record_name.append(ty+"_");
record_name.append(iod);
record_name.append("iods_");
if(write_time(record_name,num,tm.Get())<0)
{
perror("in spfs write random record time err.");
exit(-1);
}
exit(0);
}
else
{
perror("read file to string err.");
exit(-1);
}
}
break;
default:
perror("err type.");
}
}
if(flag==2)//pvfs
{
switch(type)
{
case 1://upload whole file
{
if(produce_data(file_name,size,file)==-1)
{
perror("produce data err.");
exit(-1);
}
string fname;
fname.clear();
fname.append("/mnt/pvfs2/");
fname.append(file_name);
int cr_fd=creat(fname.c_str(),666);
if(cr_fd==-1)
{
perror("in pvfs upload pvfs_creat err.");
exit(-1);
}
close(cr_fd);
int op_fd=open(fname.c_str(),O_WRONLY|O_APPEND);
if(op_fd==-1)
{
perror("in pvfs upload pvfs_open err.");
exit(-1);
}
int local_fd=open(file_name.c_str(),O_RDONLY);
if(local_fd==-1)
{
perror("in pvfs open local file to read err.");
close(op_fd);
exit(-1);
}
char ch[4*1024]={'\0'};
int ret;
tm.Start();
while((ret=read(local_fd,ch,sizeof(ch)))>0)
{
write(op_fd,ch,ret);
memset(ch,'\0',sizeof(ch));
}
tm.Stop();
if(ret==-1)
{
perror("in pvfs upload read err.");
}
close(local_fd);
close(op_fd);
//record time here.
record_name.clear();
string nm;
if(file==1)//bigfile
{
nm.append("bigfile");
}
if(file==0)//smallfile
{
nm.append("smallfile");
}
char iod[8]={'\0'};
snprintf(iod,sizeof(iod),"%02d",iod_num);
record_name.append("pvfs2_write_");
record_name.append(nm);
record_name.append("_upload_");
string ty;
if(file==1)
ty.append("MB");
if(file==0)
ty.append("KB");
char s[8]={'\0'};
snprintf(s,sizeof(s),"%02d",size);
record_name.append(s);
record_name.append(ty+"_");
record_name.append(iod);
record_name.append("iods_");
if(write_time(record_name,num,tm.Get())<0)
{
perror("in pvfs write upload record time err.");
exit(-1);
}
exit(0);
}
break;
case 2://append write
{
if(produce_data(file_name,size,file)==-1)
{
perror("produce data err.");
exit(-1);
}
string data_app;
File::ReadFileToString(file_name.c_str(),&data_app);
string name;
name.clear();
name.append("/mnt/pvfs2/");
name.append(file_name);
int op_fd=open(name.c_str(),O_APPEND|O_WRONLY);
if(op_fd==-1)
{
perror("in pvfs append write pvfs_open err.");
exit(-1);
}
int ret;
if(data_app.size()!=0)//get data to append
{
tm.Start();
ret=write(op_fd,const_cast<char *>(data_app.c_str()),data_app.size());
tm.Stop();
close(op_fd);
}
else
{
perror("get no data to append.");
close(op_fd);
exit(-1);
}
if(file==1)
cout<<"pvfs append write="<<ret/(1024*1024)<<"MB."<<endl;
if(file==0)
cout<<"pvfs append write="<<ret/1024<<"KB."<<endl;
//record time
record_name.clear();
string nm;
if(file==1)//bigfile
{
nm.append("bigfile");
}
if(file==0)//smallfile
{
nm.append("smallfile");
}
char iod[8]={'\0'};
snprintf(iod,sizeof(iod),"%02d",iod_num);
record_name.append("pvfs2_write_");
record_name.append(nm);
record_name.append("_append_");
string ty;
if(file==1)
ty.append("MB");
if(file==0)
ty.append("KB");
char s[8]={'\0'};
snprintf(s,sizeof(s),"%02d",size);
record_name.append(s);
record_name.append(ty+"_");
record_name.append(iod);
record_name.append("iods_");
if(write_time(record_name,num,tm.Get())<0)
{
perror("in pvfs write append record time err.");
exit(-1);
}
exit(0);
}
break;
case 3://random write
{
if(produce_data(file_name,size,file)==-1)
{
perror("produce data err.");
exit(-1);
}
string app_data;
File::ReadFileToString(file_name.c_str(),&app_data);
if(app_data.size()==0)
{
perror("get data err.");
exit(-1);
}
size_t offset;
size_t len;
struct stat buf;
memset(&buf,0,sizeof(struct stat));
string fname;
fname.clear();
fname.append("/mnt/pvfs2/");
fname.append(file_name);
int r=stat(const_cast<char *>(fname.c_str()),&buf);
if(r==-1)
{
perror("pvfs_stat err.");
exit(-1);
}
size_t file_size=buf.st_size;
if(file==1)//bigfile
{
file_size=(file_size/(1024*1024));
cout<<"file_size="<<file_size<<"MB."<<endl;
}
if(file==0)//smallfile
{
file_size=file_size/1024;
cout<<"file_size="<<file_size<<"KB."<<endl;
}
srand(time(NULL));
size_t un=rand()%file_size;
if(file==1)//bigfile
{
un=un*1024;
}
offset=(un*1024);
if(file==1)
cout<<"pvfs the random start offset="<<offset/(1024*1024)<<"MB"<<endl;
if(file==0)
cout<<"pvfs the random start offset="<<offset/1024<<"KB"<<endl;
int op_fd=open(fname.c_str(),O_WRONLY);
if(op_fd==-1)
{
perror("in pvfs random write pvfs_open err. ");
exit(-1);
}
int ret;
tm.Start();
lseek(op_fd,offset,SEEK_SET);
ret=write(op_fd,const_cast<char *>(app_data.c_str()),app_data.size());
tm.Stop();
close(op_fd);
if(file==1)
cout<<"pvfs random write="<<ret/(1024*1024)<<endl;
if(file==0)
cout<<"pvfs random write="<<ret/1024<<endl;
//record time
record_name.clear();
string nm;
if(file==1)//bigfile
{
nm.append("bigfile");
}
if(file==0)//smallfile
{
nm.append("smallfile");
}
char iod[8]={'\0'};
snprintf(iod,sizeof(iod),"%02d",iod_num);
record_name.append("pvfs2_write_");
record_name.append(nm);
record_name.append("_random_");
string ty;
if(file==1)
ty.append("MB");
if(file==0)
ty.append("KB");
char s[8]={'\0'};
snprintf(s,sizeof(s),"%02d",size);
record_name.append(s);
record_name.append(ty+"_");
record_name.append(iod);
record_name.append("iods_");
if(write_time(record_name,num,tm.Get())<0)
{
perror("in pvfs write random record time err.");
exit(-1);
}
exit(0);
}
break;
default:
{
perror("err write type.");
exit(-1);
}
}
}
return 0;
}
后记:
我主要是参考了pvfs1.6的分布式架构,研发了一个自己的分布式文件系统原型SDFS。
并跟pvfs2.8以及pvfs1.6做了性能对比。实验结果就暂时不贴出来了。