P2psim源代码分析四
Kejieleung
Channel与RPC
这次重点分析Channel与RPC。Channel作为底层task的数据通道,用于传输task之间的数据。channel结点定义为(注意在定义task.h里,还有相关的操作):
struct Channel
{
unsigned int bufsize;
unsigned int elemsize;
unsigned char *buf;
unsigned int nbuf;
unsigned int off;
Altarray asend;
Altarray arecv;
char *name;
};
在Packet、RPCHandle、Network、EventQueue里都有使用到.
EventQueue的构造函数如下:
EventQueue::EventQueue() : _time(0)
{
//kejie: Channel *_gochan;
_gochan = chancreate(sizeof(Event*), 0);
assert(_gochan);
//kejie:add self run to the threadmanage
thread();
}
之后再通过EventQueue::run()线程函数时调用 recvp(_gochan) 接收底层task发送到channel的数据。详细的实现可以进一步看看task里的定义,不过反正这部分不是重点,只要了解一下就可以。
一般意义的RPC使用client/server模型。请求程序是client,而服务提供程序则为server。就像一般的本地过程调用一样,且RPC是一个同步操作,直到远程过程结果返回请求程序才可以挂起。在P2PSim里的RPC机制在是单机里的模拟。通过底层的channel来传递请求。先看看RPCHandle的定义:
class RPCHandle { public:
RPCHandle(Channel*, Packet*);
~RPCHandle();
Channel *channel() { return _c; }
Packet *packet() { return _p; }
private:
Channel* _c;
Packet* _p;
};
定义很简单,主要是看用法和相关操作,集中在Node.h里使用,且多数操作是以模板形式,还用到了成员函数指针,所以看起来比较费劲呵呵,不过没关系,慢慢来。关于Node类的详细分析留在下一章进行,这里先抽出RPC相关操作与数据结构。
相关操作有:
// Send an RPC from a Node on one Node to a method
// of the same Node sub-class with a different ip
template<class BT, class AT, class RT>
bool doRPC(IPAddress dst, void (BT::* fn)(AT *, RT *), AT *args, RT *ret, Time timeout = 0)
// Same as doRPC, but this one is asynchronous
template<class BT, class AT, class RT>
unsigned asyncRPC(IPAddress dst,
void (BT::* fn)(AT *, RT *), AT *args, RT *ret, Time timeout = 0, unsigned token = 0)
// returns one of the RPCHandle's for which a reply has arrived. BLOCKING.( Used in asyncRPC)
unsigned rcvRPC(RPCSet*, bool&);
void _deleteRPC(unsigned);
typedef set<unsigned> RPCSet;
HashMap<unsigned, RPCHandle*> _rpcmap;
// RPC machinery
template<class BT, class AT, class RT>
class Thunk
bool _doRPC(IPAddress, void (*fn)(void *), void *args, Time timeout = 0);
RPCHandle* _doRPC_send(IPAddress, void (*)(void *), void (*)(void*), void *, Time = 0);
bool _doRPC_receive(RPCHandle*);
// creates a Thunk object with the necessary croft for an RPC
//kejie: Node::Thunk<BT, AT, RT> * 为返回值
template<class BT, class AT, class RT>
Node::Thunk<BT, AT, RT> *
_makeThunk(IPAddress dst, BT *target, void (BT::*fn)(AT*, RT*),AT *args, RT *ret)
具体的实现都在Node.h里,由于部分模板函数比较复杂,等到具体协议实现时再回过头来分析,这里先放一下,只分析一下recvRPC析流程。
recvRPC用于返回一个已有回应到达的RPCHandle,注释里说是一个阻塞操作,理解为有立即返回结果即可:
(1) 根据hset里标识的索引,在_rpcmap里找出要接收信息的channel信息,记录在Alt数组里
Alt定义为(task.h):
struct Alt
{
Channel *c;
void *v;
unsigned int op;
Task *task;
Alt *xalt;
};
注意还有一个alt是定义为
#define alt chanalt
int chanalt(Alt *alts);
(2) 传入alt(a)->[ chanalt(Alt*a) ] 处理执行
(3) 在alt中,首先要确定传入的alt的长度,设定为taskrunning
(4) 检查每个a[i]是否可运行,记录可运行的数目,如果多于一个就随机选择一个
(5) 确认是否有数据返回,再删除索引
详细代码如下:
整体的RPC操作可以参考以下UML图,再对照源代码:
(由于暂时上传不了图片,以后再补上)
也可以留下E-mail我将word版本传过去哈~~~最好直接发到我的邮箱因为有时没上空间也不知道呢呵呵~~