Linux内核RPC请求过程
时间:2022-04-03 09:02
/*
*/
svc_process(struct svc_rqst *rqstp)
// 这是一块缓存,server从网卡中接收到RPC消息后存放在这里(已经去掉了IP头,TCP或UDP报文头)
struct kvec *argv = &rqstp->rq_arg.head[0];
struct kvec *resv = &rqstp->rq_res.head[0];
u32 dir;
/*
* Initially it has just one page
rqstp->rq_resused = 1;
resv->iov_base = page_address(rqstp->rq_respages[0]);
rqstp->rq_res.pages = rqstp->rq_respages + 1;
rqstp->rq_res.page_base = 0;
rqstp->rq_res.buflen = PAGE_SIZE;
rqstp->rq_res.tail[0].iov_len = 0;
rqstp->rq_xid = svc_getu32(argv); // 这里開始解析RPC报文了,依照RPC报文格式,这是RPC消息的XID。
dir = svc_getnl(argv); // 这个函数解析出了RPC保本中的第二个字段(Message Type)
/* direction != CALL */
serv->sv_stats->rpcbadfmt++;
return 0;
// 返回值1表示处理过程正常,已经正常填充了RPC应答消息,能够发送给client了。
/* Returns 1 for send, 0 for drop */
return svc_send(rqstp); // 这是发送RPC应答消息的报文,不会深入分析这个函数了。
svc_drop(rqstp); // 丢弃RPC报文。
}
svc_putu32(resv, rqstp->rq_xid); // 组装RPC应答报文中的第一个字段XID
svc_putnl(resv, 1); // 组装RPC应答报文中第二个字段Message Type,RPC应答消息中这个字段固定为1.
// 眼下通用的RPC版本号是2,server端仅仅支持RPC版本号2。假设不是版本号2,则不处理了。
goto err_bad_rpc;
// 组装RPC应答消息的第三个字段 Reply State,这里临时将这个字段设置为0了,表示正常。
// reply_statp就是指向了这个字段的位置,当出错后能够依据reply_statp指针找到这个字段。
svc_putnl(resv, 0); /* ACCEPT */
// 解析RPC请求消息中第4个字段Program,这个字段是RPC服务程序(如NFS服务)的编号
// 解析RPC请求消息中第5个字段Version,这个字段是RPC服务程序的版本
// 解析RPC请求消息中第6个字段Procedure,这个字段是RPC服务例程的编号
auth_res = svc_authenticate(rqstp, &auth_stat);
if (auth_res == SVC_OK && progp) {
auth_res = progp->pg_authenticate(rqstp);
int
{
struct auth_ops *aops;
*authp = rpc_auth_ok;
// 前面的函数已经解析出了RPC请求报文中前6个字段,以下该解析第7个字段Credential了。
flavor = svc_getnl(&rqstp->rq_arg.head[0]);
dprintk("svc: svc_authenticate (%d)\n", flavor);
spin_lock(&authtab_lock);
// authtab[flavor]是这样的认证方式的操作函数集合
!try_module_get(aops->owner)) {
*authp = rpc_autherr_badcred; // 不支持这样的认证方式,这是错误码
}
// 调用详细认证方式中的accept()函数解析RPC报文中的认证信息。
return aops->accept(rqstp, authp);
enum rpc_auth_flavors {
RPC_AUTH_UNIX = 1,
RPC_AUTH_DES = 3,
RPC_AUTH_GSS = 6,
/* pseudoflavors: */
RPC_AUTH_GSS_KRB5I = 390004,
RPC_AUTH_GSS_LKEY = 390006,
RPC_AUTH_GSS_LKEYP = 390008,
RPC_AUTH_GSS_SPKMI = 390010,
};
struct auth_ops {
struct module *owner;
int (*accept)(struct svc_rqst *rq, __be32 *authp);
void (*domain_release)(struct auth_domain *);
};
progp = serv->sv_program; // 取出这个端口中注冊的RPC服务处理程序
// 每一个端口上能够注冊多种RPC服务,这些RPC服务的处理程序构成了一个链表,
for (progp = serv->sv_program; progp; progp = progp->pg_next)
break;
// progp就是找到的处理程序。假设遍历到链表结尾也沒有找到编号相等的处理程序,
if (progp == NULL) // 假设沒有处理例程,退出。
if (vers >= progp->pg_nvers ||
goto err_bad_vers;
// versp是RPC处理程序中的一个版本号,每一个版本号的处理程序中包括多个处理例程,
// procp->pc_func就是这个处理例程的处理函数了,假设server端沒有实现这个例程,
procp = versp->vs_proc + proc; // 取出处理例程
goto err_bad_proc;
serv->sv_stats->rpccnt++;
/* Build the reply header. */
// 这是RPC应答消息的第5个字段Accept State,先初始化为RPC_SUCCESS。
svc_putnl(resv, RPC_SUCCESS); // 认证通过
struct svc_procedure {
svc_procfunc pc_func; /* process the request */
// 这个函数负责解析这些内容
// 这是RPC请求的编码函数,server端须要将pc_func的处理结果封装到
kxdrproc_t pc_encode; /* XDR encode result */
kxdrproc_t pc_release; /* XDR free result */
unsigned int pc_argsize; /* argument struct size */
unsigned int pc_ressize; /* result struct size */
unsigned int pc_count; /* call count */
// 就不处理了,直接将缓存中的数据返回给client就能够了。
// 这是调整RPC应答消息缓存的一个数据量
};
struct svc_version {
u32 vs_vers; /* version number */
u32 vs_nproc; /* number of procedures */
// 这个版本号中全部例程的处理函数都在这里,各个例程按顺序排列。
// 这也是从组装应答消息相关的缓存的一个长度
unsigned int vs_hidden : 1; /* Don‘t register with 端口mapper.
* A return value of 0 means drop the request.
*/
// NFS中这个函数是nfsd_dispatch().
};
struct svc_program {
struct svc_program * pg_next; /* other programs (same xprt) */
u32 pg_prog; /* program number */
unsigned int pg_lovers; /* lowest version */
unsigned int pg_hivers; /* lowest version */
unsigned int pg_nvers; /* number of versions */
struct svc_version ** pg_vers; /* version array */
char * pg_name; /* service name */
char * pg_class; /* class name: services sharing authentication */
struct svc_stat * pg_stats; /* rpc statistics */
int (*pg_authenticate)(struct svc_rqst *);
/* Call the function that processes the request. */
/* Decode arguments */
// 開始解码了,解析RPC报文中的数据
goto err_garbage;
// 处理请求
if (rqstp->rq_dropme) {
procp->pc_release(rqstp, NULL, rqstp->rq_resp);
}
if (*statp == rpc_success &&
!xdr(rqstp, resv->iov_base+resv->iov_len, rqstp->rq_resp)) {
/* serv->sv_stats->rpcsystemerr++; */
}
dprintk("svc: calling dispatcher\n");
/* Release reply info */
procp->pc_release(rqstp, NULL, rqstp->rq_resp);
}
}
经过步骤1--步骤4的处理,我们已经解析了RPC请求报头的数据,找到了RPC请求的处理函数,最后一步就是開始处理这个请求了。处理一个RPC请求的函数是svc_version结构中的vs_dispatch函数。假设RPC程序未定义这个函数,就依照标准的流程进行处理。在标准的流程中,首先调用svc_procedure结构中的pc_decode函数,这个函数的内容是解析RPC报文的净荷,对于NFS服务来说,这个函数的作用就是解析RPC报文中的NFS数据,这些数据就是处理函数的參数。真正的处理函数是svc_procedure结构中的pc_func函数,每一个例程都须要定义自己的处理函数。处理完毕后,须要将处理结果封装在RPC应答报文中返回给client。比方对于READ操作,我们须要将读取的数据封装在RPC报文中返回,这个封装过程是由svc_procedure结构中的pc_encode函数实现的。
NFS服务定义了自己的vs_dispatch函数,NFSV2、NFSV3、NFSV4使用了同一个vs_dispatch函数,这个函数的定义是nfsd_dispatch,这个函数定义在fs/nfsd/nfssvc.c中,处理流程基本上和上面讲的流程同样,就不解说了。
Linux内核RPC请求过程,布布扣,bubuko.com