Glusterfs之nfs模块源码分析.docx
《Glusterfs之nfs模块源码分析.docx》由会员分享,可在线阅读,更多相关《Glusterfs之nfs模块源码分析.docx(21页珍藏版)》请在冰豆网上搜索。
Glusterfs之nfs模块源码分析
Glusterfs之rpc模块源码分析(上)之RPC概述
一、RPC概述
第一节、RPC结构网络
RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。
RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。
在OSI网络通信模型中,RPC跨越了传输层和应用层。
RPC使得开发包括网络分布式多程序在内的应用程序更加容易。
RPC采用客户机/服务器模式。
请求程序就是一个客户机,而服务提供程序就是一个服务器。
首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。
在服务器端,进程保持睡眠状态直到调用信息的到达为止。
当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。
目前,有多种 RPC 模式和执行。
最初由 Sun 公司提出。
IETF ONC 宪章重新修订了 Sun 版本,使得 ONC RPC 协议成为 IETF 标准协议。
现在使用最普遍的模式和执行是开放式软件基础的分布式计算环境(DCE)。
第二节、协议结构
远程过程调用(RPC)信息协议由两个不同结构组成:
调用信息和答复信息。
信息流程如下所示:
RPC:
远程过程调用流程
RPC 调用信息:
每条远程过程调用信息包括以下无符号整数字段,以独立识别远程过程:
程序号(Program number)
程序版本号(Program version number)
过程号(Procedure number)
RPC 调用信息主体形式如下:
struct call_body {
unsigned int rpcvers;
unsigned int prog;
unsigned int vers;
unsigned int proc;
opaque_auth cred;
opaque_auth verf;
1 parameter
2 parameter . . . };
RPC 答复信息:
RPC 协议的答复信息的改变取决于网络服务器对调用信息是接收还是拒绝。
答复信息请求包括区别以下情形的各种信息:
RPC 成功执行调用信息。
.
RPC 的远程实现不是协议第二版,返回 RPC 支持的最低和最高版本号。
在远程系统中,远程程序不可用。
远程程序不支持被请求的版本号。
返回远程程序所支持的最低和最高版本号。
请求的过程号不存在。
通常是呼叫方协议或程序差错。
RPC答复信息形式如下:
enum reply_stat stat
{MSG_ACCEPTED = 0,
MSG_DENIED = 1 };
第三节、工作原理
运行时,一次客户机对服务器的RPC调用,其内部操作大致有如下十步:
1.调用客户端句柄;执行传送参数
2.调用本地系统内核发送网络消息
3.消息传送到远程主机
4.服务器句柄得到消息并取得参数
5.执行远程过程
6.执行的过程将结果返回服务器句柄
7.服务器句柄返回结果,调用远程系统内核
8.消息传回本地主机
9.客户句柄由内核接收消息
10.客户接收句柄返回的数据
第四节、RPC OVER HTTP
Microsoft RPC-over-HTTP 部署(RPC over HTTP)允许RPC 客户端安全和有效地通过Internet 连接到RPC 服务器程序并执行远程过程调用。
这是在一个名称为RPC-over-HTTP 代理,或简称为RPC 代理的中间件的帮助下完成的。
RPC 代理运行在IIS 计算机上。
它接受来自Internet 的RPC 请求,在这些请求上执行认证,检验和访问检查,如果请求通过所有的测试,RPC 代理将请求转发给执行真正处理的RPC 服务器。
通过RPC over HTTP,RPC 客户端不和服务器直接通信,它们使用RPC 代理作为中间件。
Glusterfs之nfs模块源码分析(下)之NFS协议之RPC的实现和NFS协议内容
六、NFS协议之RPC的实现
因为nfs服务器启动时的端口是不确定的,所以nfs服务器将自己的端口注册到rpc服务,客户端通过rpc请求知道nfs服务器的监听端口。
下面就分析整个rpc的处理过程。
现在假设客户端有一个rpc请求达到服务器端了,通过上面nfs协议初始化的分析知道:
所有的数据读写事件都是在函数nfs_rpcsvc_conn_data_handler中处理,因为是客户端发送来的请求数据,所以执行的是epoll_in事件处理相关代码,这些事件的处理都是在函数nfs_rpcsvc_conn_data_poll_in中,这个函数实现如下:
1intnfs_rpcsvc_conn_data_poll_in(rpcsvc_conn_t*conn)
2
3{
4
5ssize_tdataread=-1;
6
7size_treadsize=0;
8
9char*readaddr=NULL;
10
11intret=-1;
12
13readaddr=nfs_rpcsvc_record_read_addr(&conn->rstate);//rpc服务记录开始读取数据的地址
14
15readsize=nfs_rpcsvc_record_read_size(&conn->rstate);//rpc服务记录数据需要读取的长度
16
17dataread=nfs_rpcsvc_socket_read(conn->sockfd,readaddr,readsize);//从socket中读出记录数据
18
19if(dataread>0)
20
21ret=nfs_rpcsvc_record_update_state(conn,dataread);//根据读取的数据处理
22
23returnret;
24
25}
上面代码首先会根据rpc服务记录中的接收数据类型来判断接收什么数据,主要是分为头部消息和正式的rpc消息,正式的rpc消息的长度是通过头部消息中给出的,所以接收消息的步骤一般是先头部消息,然后正式的rpc调用消息,否则就是视为错误的消息,然后根据消息的长度从socket中读出消息到rpc服务记录的结构体的成员变量中,最后交给函数nfs_rpcsvc_record_update_state处理,它根据读取的数据来处理整个rpc的过程,包括xdr(外部数据表示)和根据消息获取调用的函数并且执行函数,具体实现如下:
1intnfs_rpcsvc_record_update_state(rpcsvc_conn_t*conn,ssize_tdataread)
2
3{
4
5rpcsvc_record_state_t*rs=NULL;
6
7rpcsvc_t*svc=NULL;
8
9rs=&conn->rstate;
10
11if(nfs_rpcsvc_record_readfraghdr(rs))//根据rpc服务的记录状态是否读取头部消息
12
13dataread=nfs_rpcsvc_record_update_fraghdr(rs,dataread);//读取消息头部
14
15if(nfs_rpcsvc_record_readfrag(rs)){//是否读取后面的数据
16
17if((dataread>0)&&(nfs_rpcsvc_record_vectored(rs))){//是否读取向量片段(
18
19dataread=nfs_rpcsvc_handle_vectored_frag(conn,dataread);//处理向量片段数据
20
21}elseif(dataread>0){
22
23dataread=nfs_rpcsvc_record_update_frag(rs,dataread);//更新rpc服务记录的片段数据
24
25}
26
27}
28
29if((nfs_rpcsvc_record_readfraghdr(rs))&&(rs->islastfrag)){//如果下一条消息是头部消息且是最后一帧
30
31nfs_rpcsvc_handle_rpc_call(conn);//处理rpc调用
32
33svc=nfs_rpcsvc_conn_rpcsvc(conn);//链接对象引用加1
34
35nfs_rpcsvc_record_init(rs,svc->ctx->iobuf_pool);//重新初始化rpc服务记录的状态信息
36
37}
38
39return0;
40
41}
整个函数首先读取协议信息的头部消息,读取完头部信息以后更新rpc服务记录状态,然后根据更新的状态继续读取头部信息后面的消息,后面的消息分为两种情况来读取,一般第一次来的是一个头部消息,这个消息中记录了下一次需要读取的消息的长度,也就是正式的rpc调用信息的长度。
所以当第二次消息响应来的时候就是正式消息,根据不同的消息有不同的处理方式。
头部消息处理方式主要是为接收正式的消息做一些初始化和准备工作(例如数据的长度和类型等)。
如果头部消息则不会执行处理rpc的调用函数,因为它必须要接收到rpc调用消息以后才能处理。
下面继续分析处理rpc调用的函数nfs_rpcsvc_handle_rpc_call,因为它是处理整个rpc调用的核心,它的实现如下:
1intnfs_rpcsvc_handle_rpc_call(rpcsvc_conn_t*conn)
2
3{
4
5rpcsvc_actor_t*actor=NULL;
6
7rpcsvc_request_t*req=NULL;
8
9intret=-1;
10
11req=nfs_rpcsvc_request_create(conn);//动态创建一个rpc服务请求对象(结构体)
12
13if(!
nfs_rpcsvc_request_accepted(req))//是否接受rpc服务请求
14
15;
16
17actor=nfs_rpcsvc_program_actor(req);//得到rpc服务调用过程的描述对象
18
19if((actor)&&(actor->actor)){
20
21THIS=nfs_rpcsvc_request_actorxl(req);//得到请求的xlator链表
22
23nfs_rpcsvc_conn_ref(conn);//链接状态对象的引用加1
24
25ret=actor->actor(req);//执行函数调用
26
27}
28
29returnret;
30
31}
这个函数首先根据链接状态对象创建一个rpc服务请求的对象,然后根据rpc服务请求对象得到一个rpc服务调用过程的描述对象,最后就根据这个描述对象执行具体的某一个rpc远程调用请求。
下面在看看怎样根据连接状态对象创建rpc服务请求对象的,nfs_rpcsvc_request_create函数实现如下:
1rpcsvc_request_t*nfs_rpcsvc_request_create(rpcsvc_conn_t*conn)
2
3{
4
5char*msgbuf=NULL;
6
7structrpc_msgrpcmsg;
8
9structiovecprogmsg;/*RPCProgrampayload*/
10
11rpcsvc_request_t*req=NULL;
12
13intret=-1;
14
15rpcsvc_program_t*program=NULL;
16
17nfs_rpcsvc_alloc_request(conn,req);//从内存池中得到一个权限请求对象并且初始化为0
18
19msgbuf=iobuf_ptr(conn->rstate.activeiob);//从激活的IO缓存得到一个用于消息存放的缓存空间
20
21//从xdr数据格式转换到rpc数据格式
22
23ret=nfs_xdr_to_rpc_call(msgbuf,conn->rstate.recordsize,&rpcmsg,
24
25&progmsg,req->cred.authdata,req->verf.authdata);
26
27nfs_rpcsvc_request_init(conn,&rpcmsg,progmsg,req);//根据上面转换的消息初始化rpc服务请求对象
28
29if(nfs_rpc_call_rpcvers(&rpcmsg)!
=2){//rpc协议版本是否支持
30
31;
32
33}
34
35ret=__nfs_rpcsvc_program_actor(req,&program);//根据程序版本号得到正确的rpc请求描述对象
36
37req->program=program;
38
39ret=nfs_rpcsvc_authenticate(req);//执行权限验证函数调用验证权限
40
41if(ret==RPCSVC_AUTH_REJECT){//是否被权限拒绝
42
43;
44
45}
46
47returnreq;
48
49}
通过上面的函数调用就得到了一个正确版本的rpc服务远程调用程序的描述对象,后面会根据这个对象得到对应的远程调用函数的描述对象,这个是通过下面这个函数实现的:
1rpcsvc_actor_t*nfs_rpcsvc_program_actor(rpcsvc_request_t*req)
2
3{
4
5interr=SYSTEM_ERR;
6
7rpcsvc_actor_t*actor=NULL;
8
9actor=&req->program->actors[req->procnum];//根据函数id得到正确的函数调用对象
10
11returnactor;
12
13}
这里得到的函数调用对象就会返回给调用程序,调用程序就会具体执行远程过程调用了。
到此一个完整的rpc调用以及一个nfs服务就完成了,nfs服务器就等待下一个请求,整个过程可谓一波三折,整个过程绕了很大一个圈。
下面通过一个图来完整描述整个过程:
附件1 NFS Protocol Family
NFS Protocol Family
The NFS protocol suite includes the following protocols:
MNTV1
Mount protocol version 1, for NFS version 2
Mntv3
Mount protocol version 3, for NFS version 3
NFS2
Sun Network File system version 2
NFS3
Sun Network File system version 3
NFSv4
Sun Network File system version 4
NLMv4
Network Lock Manager version 4
NSMv1
Network Status Monitor protocol
MNTV1:
ftp:
//ftp.rfc-editor.org/in-notes/rfc1094.txt.
The Mount protocol version 1 for NFS version 2 (MNTv1) is separate from, but related to, the NFS protocol. It provides operating system specific services to get the NFS off the ground -- looks up server path names, validates user identity, and checks access permissions. Clients use the Mount protocol to get the first file handle, which allows them entry into a remote filesystem.
The Mount protocol is kept separate from the NFS protocol to make it easy to plug in new access checking and validation methods without changing the NFS server protocol.
Notice that the protocol definition implies stateful servers because the server maintains a list of client's mount requests. The Mount list information is not critical for the correct functioning of either the client or the server. It is intended for advisory use only, for example, to warn possible clients when a server is going down.
Version one of the Mount protocol is used with version two of the NFS protocol. The only information communicated between these two protocols is the "fhandle" structure. The header structure is as follows:
8
7
6
5
4
3
2
1
Octets
Directory Path Length
1
2
3
4
Directory Path Name
5-N
Directory Path Length:
The directory path length.
Directory Path Name:
The directory path name.
Mntv3:
ftp:
//ftp.rfc-editor.org/in-notes/rfc1813.txt.
The supporting Mount protocol version 3 for NFS version 3 protocol performs the operating system-specific functions that allow clients to attach remote directory trees to a point within the local file system. The Mount process also allows the server to grant remote access privileges to a restricted set of clients via export control.
The Lock Manager provides support for file locking when used in the NFS environment. The Network Lock Manager (NLM) protocol isolates the inherently stateful aspects of file locking into a separate protocol. A complete description of the above protocols and their implementation is to be found in [X/OpenNFS].
The normative text is the description of the RPC procedures and arguments and results, which defines the over-the-wire protocol, and the semantics of those procedures. The material describing implementation practice aids the understanding of the protocol specification and describes some possible implementation issues and solutions. It is not possible to describe all implementations and the UNIX operating system implementation of the NFS version 3 protocol is most often used to provide examples. The structure of the protocol is as follows.
8
7
6
5
4
3
2
1
Octets
Directory Path Length
1
2
3
4
Directory Path Name
5-N
Directory path length:
The directory path length.
Directory Path Name:
The directory path name
NFS2:
ftp:
//ftp.rfc-editor.org/in-notes/rfc1