P2psim 源代码分析(一)
kejieleung
之前暑假本来就开始研究的了,不过后来搞其它项目去,p2psim的研究就放了一段时间了,现在总算能继续看啦哈,很多资料记录了在笔记上,因为放得比较久,现在是一边整理一边再熟悉咯。
网上关系这个仿真的资料真的比较少,而且本身项目在03年之后就没有更新了,不过p2psim的好处是,已经有几个常用的模型的实现,做实验时可以直接拿来分析,再与自己的模型作比较。当然,要能在上面实现自己的模型,还得先明白p2psim的运行机制呀。
慢慢分析啦哈~~~~可能有些不正确的地方,欢迎大家指正、讨论。P2p的配置和架构、类关系可以看我之前发的两篇文章
p2psim学习笔记(一) 編译运行
http://blog.csdn.net/kikikind/archive/2008/08/03/2760576.aspx
p2psim学习笔记(二) 总体框架流程
http://blog.csdn.net/kikikind/archive/2008/08/04/2765561.aspx
1. Main函数Main.c
- void
- taskmain(int argc, char *argv[])
- {
- //kejie:relevant function putenv,setenv,unsetenv #include<stdlib.h>
- //get environment variant from name
- p2psim_verbose = getenv("P2PSIM_DEBUG") ? atoi(getenv("P2PSIM_DEBUG")) : 0;
- //kejie:set random seed
- srandom(time(0) ^ (getpid() + (getpid() << 15)));
- parse_args(argc, argv);
- //add in the optional args from parse_args
- //kejie:here just initialize the object
- //<=static Args args() { return _args; } <= static Args _args; <=class Args : public hash_map<string,string>
- Args a = Node::args ();
- //kejie: in parse_args(-o) ( options.push_back (optarg);)
- for (unsigned int i = 0; i < options.size (); i++) {
- //kejie: split - split a file into pieces( define in parse.c )
- vector<string> x = split (options[i], "=");
- a.insert(make_pair(x[0], x[1]));
- }
- Node::set_args (a);
- // Put a protocol on all these nodes.
- Node::parse(protocol_file);
- //kejie:注册自定义signal
- signal( SIGUSR1, handle_signal_usr1 );
- // Creates a network with the appropriate underlying topology and give Network
- // a chance to eat them all.
- //kejie:在 parse中还会实例化 Network( Network::Instance(top, failure_model); )
- Topology::parse(topology_file);
- while(anyready())
- yield();
- //kejie:RTT- Round Trip Time 往返时间
- DEBUG(0) << "average RTT = " << Network::Instance()->avglatency()*2 << "ms" << endl;
- // make sure the network ate all the nodes
- while(anyready())
- yield();
- // Creates an event queue, parses the file, etc.
- // Will fire off the EventQueue
- //kejie:处理event_file
- EventGenerator::parse(event_file);
- // Initialize all protocols
- if (Node::init_state()) {
- const set<Node*> *all = Network::Instance()->getallnodes();
- for(set<Node*>::const_iterator i = all->begin(); i != all->end(); ++i)
- (*i)->initstate(); //初始化结点,在Node不提供实现,留在具体实现的协议上实现
- }
- }
(1) P2p的底层是由libtask实现的模型,不是熟悉的线程,这个taskmain就是作为程序启运后的第一个任务来运行。真正的main函数位置/libtask/task.c
- static void
- taskmainstart(void *v)
- {
- taskmain(taskargc, taskargv);
- }
- int
- main(int argc, char **argv)
- {
- argv0 = argv[0];
- taskargc = argc;
- taskargv = argv;
- if(mainstacksize == 0)
- mainstacksize = 256*1024;
- //主任务即在p2psim/main.c
- taskcreate(taskmainstart, nil, mainstacksize);
- taskscheduler();
- fprint(2, "taskscheduler returned in main!/n");
- abort();
- return 0;
- }
(2) 先是解释输入参数,再根据参数指定的protocol_file, topology_file, event_file的位置分别读取文件里的所有运行参数。
(3) 这三个文件的处理函数parse()分别作为静态函数封装在Node,Topology,EventGenerator类里
(4) Task相关的部分定义在/libtask/task.h和task.c里,下面是anyready的定义:
- int
- anyready(void)
- {
- return taskrunqueue.head != nil;
- }
- 任务都放在一个taskrunqueue里,是一个Tasklist的结构,定义如下:
- struct Tasklist
- {
- Task *head;
- Task *tail;
- };
yield 有如下定义:#define yield taskyield,taskyield为一个函数,调用taskready将当前为taskrunning的task加到taskrunqueue中:
- int
- taskyield(void)
- {
- int n;
- n = tasknswitch;
- taskready(taskrunning);
- taskswitch();
- return tasknswitch - n;
- }
- void
- taskready(Task *t)
- {
- addtask(&taskrunqueue, t);
- }
Taskswitch()完成相关的运行设置,最后经taskscheduler来调度。不过这部分机制可以不用深入研究,明白怎么运行即可,有兴趣的话可以直接到定义和实现文件里了解整个流程。
(5) 这部分比较容易明白,对照上面的注释即可
2. Main函数用例图说明
这部分先分析这么多,下一篇重点分板用个处理类Node,Topology,EventGenerator类以及处理的参数文件结构。