DSDV学习

先转一段网文:

ns2的离散事件驱动原理(Scheduler,Handler,Event,Timer)

首先是Scheduler,Handler和Event类的关系。
      在NS2中,事件(Event)是基本的调度单元,比如发送一个Packet、接收一个Packet等等。每个Event都有自己的处理工具,这个工具就是一个Handler类的对象handler_。Handler中仅包含一个函数,描述对Event的处理方法,即handle(Event*e)。
      给定一个事件,Scheduler将调用schedule(Handler* h, Event* e, double delay)函数,该函数设定Event的uid_,并设定用于处理该事件的Handler:e->handler_ = h,然后将该事件插入Scheduler维护的事件队列中。一般说来,处理事件的Handler都是产生该事件的实体本身,所以我们常常可以看到schedule函数在调用的时候*h用到this指针。
      NS2运行仿真时,Scheduler::run()函数不停地运行来处理队列中的事件,队列中的事件逐个dequeue()出来,随后运行Scheduler::dispatch(Event* p, double time)函数,将一个事件从队列中弹出来,调用它对应的Handler的handle()函数处理该它。

 1 void
2 Scheduler::dispatch(Event* p, double t)
3 {
4 if (t < clock_) {
5 fprintf(stderr, "ns: scheduler going backwards in time from %f to %f.\n", clock_, t);
6 abort();
7 }
8
9 clock_ = t;
10 p->uid_ = -p->uid_; // being dispatched
11 p->handler_->handle(p); // dispatch
12 }


      这样就完成了一个事件从产生到排队到派出被处理的过程。

  接下来看一下TimerHandler的作用。
      计时器(Timer)是NS2仿真的关键手段,它用来设置一个未来的事件,在它到时后事件将被dispatch出来进行处理。Timer都是TimerHandler基类的派生类。TimerHandler与Scheduler交互的函数是sched(double delay),它调用Scheduler::schedule(Handler* h, Event* e, double delay)插入一个delay时间后的事件进入队列,Handler设置为TimerHandler本身(用this)。
      delay时间到了后,Scheduler会从事件队列中dequeue出该事件,调用其处理函数,也就是TimerHandler::handle() 。而handle()除了做一些计时器的状态设定工作外,核心的处理由虚函数expire(Event* e)来做。由C++的动态特性不难理解,expire()将在各种自定义的Timer(也就是TimerHandler的派生类)中进行重写,实现各Timer的不同处理方法。

 

转入正文:

先看Handler类的定义:

1 class Handler {
2 public:
3 virtual ~Handler () {}
4 virtual void handle(Event* event) = 0;
5 };

其中handle函数为纯虚函数,那么子类在继承Handler类时必须实现这个函数,在dsdv.cc文件中,dsdv_agent的Handler实现:

1 class DSDV_Helper : public Handler {//周期发送路由更新消息包和路由失效事件处理类DSDV_Helper 
2 public:
3 DSDV_Helper(DSDV_Agent *a_) { a = a_; }
4 virtual void handle(Event *e) { a->helper_callback(e); }
5
6 private:
7 DSDV_Agent *a;
8 };

handle函数调用了DSDV_Agent的helper_callback()函数。在函数startUp中,

 1 void
2 DSDV_Agent::startUp()//开始
3 {
4 Time now = Scheduler::instance().clock();
5
6 subnet_ = Address::instance().get_subnetaddr(myaddr_);
7 //DEBUG
8 address = Address::instance().print_nodeaddr(myaddr_);
9 //printf("myaddress: %d -> %s\n",myaddr_,address);
10
11 rtable_ent rte;
12 bzero(&rte, sizeof(rte));
13
14 rte.dst = myaddr_;
15 rte.hop = myaddr_;
16 rte.metric = 0;
17 rte.seqnum = seqno_;
18 seqno_ += 2;
19
20 rte.advertise_ok_at = 0.0; // can always advert ourselves
21 rte.advert_seqnum = true;
22 rte.advert_metric = true;
23 rte.changed_at = now;
24 rte.new_seqnum_at = now;
25 rte.wst = 0;
26 rte.timeout_event = 0; // Don't time out our localhost :)
27
28 rte.q = 0; // Don't buffer pkts for self!
29
30 table_->AddEntry (rte);
31
32 // kick off periodic advertisments
33 periodic_callback_ = new Event ();
34 Scheduler::instance ().schedule (helper_,
35 periodic_callback_,
36 jitter (DSDV_STARTUP_JITTER, be_random_));
37 }

DSDV_Agent设置一条关于自己的新路由,对于函数schedule()的实参,helper对象是DSDV_Helper类,如上所示,该类实现了handle函数。

1   helper_ = new DSDV_Helper (this);
2 trigger_handler = new DSDVTriggerHandler(this);

再看看schedule函数:

 1 void 
2 Scheduler::schedule(Handler* h, Event* e, double delay)
3 {
4 // handler should ALWAYS be set... if it's not, it's a bug in the caller
5 if (!h) {
6 fprintf(stderr,
7 "Scheduler: attempt to schedule an event with a NULL handler."
8 " Don't DO that at time %f\n", clock_);
9 abort();
10 };
11
12 if (e->uid_ > 0) {
13 printf("Scheduler: Event UID not valid!\n\n");
14 abort();
15 }
16
17 if (delay < 0) {
18 // You probably don't want to do this
19 // (it probably represents a bug in your simulation).
20 fprintf(stderr,
21 "warning: ns Scheduler::schedule: scheduling event\n\t"
22 "with negative delay (%f) at time %f.\n", delay, clock_);
23 }
24
25 if (uid_ < 0) {
26 fprintf(stderr, "Scheduler: UID space exhausted!\n");
27 abort();
28 }
29 e->uid_ = uid_++;
30 e->handler_ = h;
31 double t = clock_ + delay;
32
33 e->time_ = t;
34 insert(e);
35 }

大概知道本函数的作用是给相关事件设置处理函数,也就是说通过调用这个函数,节点在启动时,为自己设定了事件,及其处理函数(暂且只能这么说,本人理解还不透,尴尬)。

我了个去,电脑死机,没保存。当时间调度机制取出某个事件,并调用处理函数时(即helper_callback_):

 1 void
2 DSDV_Agent::helper_callback (Event * e)
3 {
4 Scheduler & s = Scheduler::instance ();
5 double now = s.clock ();
6 rtable_ent *prte;
7 rtable_ent *pr2;
8 int update_type; // we want periodic (=1) or triggered (=0) update?
9 //DEBUG
10 //printf("Triggered handler on 0x%08x\n", e);
11
12 // Check for periodic callback
13 if (periodic_callback_ && e == periodic_callback_)
14 {
15 update_type = 1;
16 Packet *p = makeUpdate(/*in-out*/update_type);
17 if (verbose_)
18 {
19 trace ("VPC %.5f _%d_", now, myaddr_);
20 tracepkt (p, now, myaddr_, "PU");
21 }
22
23
24 if (p) {
25 assert (!HDR_CMN (p)->xmit_failure_); // DEBUG 0x2
26 // send out update packet jitter to avoid sync
27 //DEBUG
28 //printf("(%d)..sendout update pkt (periodic=%d)\n",myaddr_,update_type);
29 s.schedule (target_, p, jitter(DSDV_BROADCAST_JITTER, be_random_));
30 }
31
32 // put the periodic update sending callback back onto the
33 // the scheduler queue for next time....
34 s.schedule (helper_, periodic_callback_,
35 perup_ * (0.75 + jitter (0.25, be_random_)));
36
37 // this will take the place of any planned triggered updates
38 lasttup_ = now;
39 return;
40 }
41
42 // Check for timeout
43 // If it was a timeout, fix the routing table.检查是否到时
44 for (table_->InitLoop (); (prte = table_->NextLoop ());)
45 if (prte->timeout_event && (prte->timeout_event == e))
46 break;
47
48 // If it was a timeout, prte will be non-NULL
49 // Note that in the if we don't touch the changed_at time, so that when
50 // wst is computed, it doesn't consider the infinte metric the best
51 // one at that sequence number.
52 if (prte)
53 {
54 if (verbose_)
55 {
56 trace ("VTO %.5f _%d_ %d->%d", now, myaddr_, myaddr_, prte->dst);
57 /* trace ("VTO %.5f _%d_ trg_sch %x on sched %x time %f", now, myaddr_,
58 trigupd_scheduled,
59 trigupd_scheduled ? s.lookup(trigupd_scheduled->uid_) : 0,
60 trigupd_scheduled ? trigupd_scheduled->time_ : 0); */
61 }
62
63 for (table_->InitLoop (); (pr2 = table_->NextLoop ()); )
64 {
65 if (pr2->hop == prte->dst && pr2->metric != BIG)
66 {
67 if (verbose_)
68 trace ("VTO %.5f _%d_ marking %d", now, myaddr_, pr2->dst);
69 pr2->metric = BIG;
70 pr2->advertise_ok_at = now;
71 pr2->advert_metric = true;
72 pr2->advert_seqnum = true;
73 pr2->seqnum++;
74 // And we have routing info to propogate.
75 //DEBUG
76 //printf("(%d)..we have routing info to propagate..trigger update for dst %d\n",myaddr_,pr2->dst);
77 needTriggeredUpdate(pr2, now);
78 }
79 }
80
81 // OK the timeout expired, so we'll free it. No dangling pointers.
82 prte->timeout_event = 0;
83 }
84 else
85 { // unknown event on queue 队列中未知的事件
86 fprintf(stderr,"DFU: unknown queue event\n");
87 abort();
88 }
89
90 if (e)
91 delete e;
92 }

如果是周期更新,调用makeUpdate函数,增加自己的序列号,记录下路由条目最后的跳数存放到last_advertise_metric中,并将所有的路由条目增加到数据包中。




posted @ 2012-03-31 16:45  leealways87  阅读(1264)  评论(0编辑  收藏  举报