linux rpc在内核中实现,源代码位置是net/sunrpc/
sunrpc不仅实现了rpc的调度,同时将rpc请求构造解析发送接受的通用部分进行了提取。要定义自己的rpc调用仅需要实现那些非通用的部分,下面简要介绍怎么实现一个自己的rpc客户端(2.4内核)
主要数据结构介绍
1。rpc请求
/*
* This is the RPC buffer
*/
struct rpc_iov {
struct iovec io_vec[MAX_IOVEC]; //使用10个 iovec 可以操作分散的buffer
unsigned int io_nr;
unsigned int io_len;
};
/*
* This describes a complete RPC request
*/
struct rpc_rqst {
/*
* This is the user-visible part
*/
struct rpc_xprt * rq_xprt; /* RPC client */
struct rpc_timeout rq_timeout; /* timeout parms */
struct rpc_iov rq_snd_buf; /* send buffer 发送缓冲区*/
struct rpc_iov rq_rcv_buf; /* recv buffer 接受缓冲区*/
/*
* This is the private part
*/
struct rpc_task * rq_task; /* RPC task data */
__u32 rq_xid; /* request XID */
struct rpc_rqst * rq_next; /* free list */
volatile unsigned char rq_received : 1;/* receive completed */
/*
* For authentication (e.g. auth_des)
*/
u32 rq_creddata[2];
/*
* Partial send handling
*/
u32 rq_bytes_sent; /* Bytes we have sent */
#ifdef RPC_PROFILE
unsigned long rq_xtime; /* when transmitted */
#endif
};
#define rq_svec rq_snd_buf.io_vec
#define rq_snr rq_snd_buf.io_nr
#define rq_slen rq_snd_buf.io_len
#define rq_rvec rq_rcv_buf.io_vec
#define rq_rnr rq_rcv_buf.io_nr
#define rq_rlen rq_rcv_buf.io_len
流程介绍
1。注册自己的rpc调用
#ifndef MAX
#define MAX(a, b) (((a) > (b))? (a) : (b))
#endif
#define PROC(proc, argtype, restype, timer) \
{ "xxx_clnt" #proc, \
(kxdrproc_t) xxx_clnt_encode_##argtype, \
(kxdrproc_t) xxx_clnt_decode_##restype, \
MAX(XXX_##argtype##_sz,XXX_##restype##_sz) , \
timer \
}
// 这里注册了3个rpc调用,分别是:read、write、getsize
static struct rpc_procinfo xxx_clnt_procedures[4] = {
PROC(null, void, void, 0),
PROC(read, readargs, readres, 0),
PROC(write, writeargs, writeres, 0),
PROC(getsize, getsizeargs, getsizeres, 0),
}
/*上面结构实际效果是下面这样
其中注册了每个rpc请求和返回的编码解码函数,在步骤4中描述
static struct rpc_procinfo xxx_clnt_procedures[4] = {
{"xxx_clnt_null", (kxdrproc_t)xxx_clnt_encode_void, (kxdrproc_t)xxx_clnt_decode_void, MAX(XXX_void_sz, XXX_void_sz), 0},
{"xxx_clnt_read", (kxdrproc_t)xxx_clnt_encode_readargs, (kxdrproc_t)xxx_clnt_decode_readres, MAX(XXX_readargs_sz, XXX_readres_sz), 0},
{"xxx_clnt_write", (kxdrproc_t)xxx_clnt_encode_writeargs, (kxdrproc_t)xxx_clnt_decode_writeres, MAX(XXX_writeargs_sz, XXX_writeres_sz), 0},
{"xxx_clnt_getsize", (kxdrproc_t)xxx_clnt_encode_getsizeargs, (kxdrproc_t)xxx_clnt_decode_getsizeres, MAX(XXX_getsizeargs_sz, XXX_getsizeres_sz), 0},
};
*/
static struct rpc_version xxx_clnt_version1 = {
1,
sizeof(xxx_clnt_procedures)/sizeof(xxx_clnt_procedures[0]),
xxx_clnt_procedures
};
static struct rpc_stat xxx_clnt_rpcstat;
static struct rpc_version* xxx_clnt_version[] = {
NULL,
&xxx_clnt_version1,
};
struct rpc_program xxx_clnt_program = {
"xxx",
XXX_PROGRAM,
sizeof(xxx_clnt_version)/sizeof(xxx_clnt_version[0]),
xxx_clnt_version,
&xxx_clnt_rpcstat,
};
2。在运行前先创建rpc调度线程
if (rpciod_up() != 0) {
printk("xxx:unable to run async scheduler!\n");
return -1;
}
3。实现rpc调用函数
以read为例struct xxx_readargs readargs; //用户自定义请求参数结构
struct xxx_readres readres; //用户自定义请求返回结构
struct rpc_message read_msg = {
1,
&readargs, //以指针形式注册到rpc消息结构中
&readres,
NULL,
};
// 同步的情况
int xxx_read(???)
{
int retval = 0;
// 设置发送的属性
readargs.??? = ??;
readargs.??? = ??;
// 设置返回参数的属性
readres.??? = ??;
retval = rpc_call_sync(xxx_clnt, &read_msg, 0);
if (retval < 0){
// 出错情况
} else {
// 获取返回值
}
return retval;
}
4。rpc调用参数和返回值的解析函数
步骤3中有用户自定义的参数返回值
struct xxx_readargs readargs; //用户自定义请求参数结构
struct xxx_readres readres; //用户自定义请求返回结构
在进行socket包发送接受时需要进行它们的编码和解析
主要是为了便利用户定义参数的灵活性,比如数据可以通过iovec分散的保存
下面是nfs的编码函数
// 用户自定义的请求结构指针args被传入函数
static int
nfs3_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
{
unsigned int nr;
u32 count = args->count;
// 部分属性参数的打包
p = xdr_encode_fhandle(p, args->fh);
p = xdr_encode_hyper(p, args->offset);
*p++ = htonl(count);
*p++ = htonl(args->stable);
*p++ = htonl(count);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
/* Get the number of buffers in the send iovec */
nr = args->nriov;
if (nr+2 > MAX_IOVEC) {
// rpc请求只能最大支持10个iovec
printk(KERN_ERR "NFS: Bad number of iov's in xdr_writeargs\n");
return -EINVAL;
}
/* Copy the iovec 通过拷贝iovec到将要发送的数据导入到了rpc里面*/
memcpy(req->rq_svec + 1, args->iov, nr * sizeof(struct iovec));
req->rq_slen += count;
req->rq_snr += nr;
return 0;
}








