NS事件调度器
NS是一个基于事件驱动的单线程模拟器,在模拟器中有四种可使用的调度器:链表调度器、堆调度器、日历调度器和实时调度器。调度器调度的是事件,下面是事件的定义(event):
class Event { public: Event* next_; /*事件链表*/ Event* prev_; Handler* handler_; /* 处理事件时的句柄 */ double time_; /* 事件执行的时间 */ scheduler_uid_t uid_; /* 全局唯一ID */ Event() : time_(0), uid_(0) {} /*构造函数*/ };当事件的调度时间到达时,该事件被传递给它的句柄来处理。下面是调度器类本身:
class Scheduler : public TclObject { public: static Scheduler& instance() { return (*instance_); // 通用调度器入口 } void schedule(Handler*, Event*, double delay); // 事件调度器 virtual void run(); // 执行模拟 virtual void cancel(Event*) = 0; // 删除事件 virtual void insert(Event*) = 0; // 将事件插入队列 virtual Event* lookup(scheduler_uid_t uid) = 0; // 寻找事件 virtual Event* deque() = 0; // 出队 virtual const Event* head() = 0; // 下一个事件 double clock() const { // 仿真的虚拟时间 return (clock_); } virtual void sync() {}; virtual double start() { // 开始时间 return SCHED_START; } virtual void reset(); protected: void dumpq(); // for debug: 移除并打印剩下的事件 void dispatch(Event*); // 执行一个事件 void dispatch(Event*, double); //执行事件,设定时间 Scheduler(); virtual ~Scheduler(); int command(int argc, const char*const* argv); double clock_; int halted_; static Scheduler* instance_; static scheduler_uid_t uid_; };举个例子:假设在aodv.cc里执行:
Scheduler::instance().schedule(this, &intr, HELLO_INTERVAL);它会执行common\scheduler.cc里面的schedule()函数:
void Scheduler::schedule(Handler* h, Event* e, double delay) { //如果未设置句柄,退出 if (!h) { fprintf(stderr, "Scheduler: attempt to schedule an event with a NULL handler." " Don't DO that.\n"); abort(); }; if (e->uid_ > 0) {//事件ID出错,退出 printf("Scheduler: Event UID not valid!\n\n"); abort(); } if (delay < 0) { // You probably don't want to do this // (it probably represents a bug in your simulation). fprintf(stderr, "warning: ns Scheduler::schedule: scheduling event\n\t" "with negative delay (%f) at time %f.\n", delay, clock_); } if (uid_ < 0) { fprintf(stderr, "Scheduler: UID space exhausted!\n"); abort(); } e->uid_ = uid_++; e->handler_ = h; double t = clock_ + delay; e->time_ = t; insert(e);//插入调度队列 }
insert()函数将事件插入调度队列,等待执行,NS默认使用的是日历调度器:
void CalendarScheduler::insert(Event* e) { int i; if (cal_clock_ > e->time_) { // may happen in RT scheduler cal_clock_ = e->time_; i = lastbucket_ = CALENDAR_HASH(cal_clock_); } else i = CALENDAR_HASH(e->time_); Event *head = buckets_[i].list_; Event *before=0; if (!head) { buckets_[i].list_ = e; e->next_ = e->prev_ = e; ++stat_qsize_; ++buckets_[i].count_; } else { bool newhead; if (e->time_ >= head->prev_->time_) { // insert at the tail before = head; newhead = false; } else { // insert event in time sorted order, FIFO for sim-time events for (before = head; e->time_ >= before->time_; before = before->next_) ; newhead = (before == head); } e->next_ = before; e->prev_ = before->prev_; before->prev_ = e; e->prev_->next_ = e; if (newhead) { buckets_[i].list_ = e; //assert(e->time_ <= e->next_->time_); } //assert(e->prev_ != e); if (e->prev_->time_ != e->time_) { // unique timing ++stat_qsize_; ++buckets_[i].count_; } } ++qsize_; //assert(e == buckets_[i].list_ || e->prev_->time_ <= e->time_); //assert(e == buckets_[i].list_->prev_ || e->next_->time_ >= e->time_); if (stat_qsize_ > top_threshold_) { resize(nbuckets_ << 1, cal_clock_); return; } }run函数会不断的在队列中取出事件执行:
void Scheduler::run() { instance_ = this; Event *p; while (!halted_ && (p = deque())) { dispatch(p, p->time_); } }取出队首事件,如果队列不为空,则调用dispatch()函数
void Scheduler::dispatch(Event* p, double t) { if (t < clock_) {//如果在当前时间之前执行事件,则退出 fprintf(stderr, "ns: scheduler going backwards in time from %f to %f.\n", clock_, t); abort(); } clock_ = t; p->uid_ = -p->uid_; // 事件已经被处理了,标为负 p->handler_->handle(p); // 调用事件的handle()函数 }最后一句调用了用户自己定义了handle函数:
void NeighborTimer::handle(Event*) { agent->nb_purge(); Scheduler::instance().schedule(this, &intr, HELLO_INTERVAL); }如此便完成了定时执行的功能。