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);
}如此便完成了定时执行的功能。

浙公网安备 33010602011771号