NS2的调度器Scheduler调度过程(事件的执行过程)

来源:互联网 发布:淘宝投诉电话是多少呢 编辑:程序博客网 时间:2024/05/29 09:21
(一)
1、Event的定义:
class Event {public:Event* next_;   //事件链表中指向的下一个事件的指针Event* prev_;      //事件链表中指向的下一个事件的指针Handler* handler_;     //要处理事件时调用的句柄double time_;//事件的执行时间scheduler_uid_t uid_;//唯一标识一个事件的id,在schedule()中初始化为1Event() : time_(0), uid_(0) {}    //一个事件的构造函数,初始化变量time_以及uid_};

当事件的调度时间到达时,改时间传递该它的句柄handler_来处理。

2、Schedule类的定义:

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: remove + print remaining eventsvoid dispatch(Event*);// execute an eventvoid dispatch(Event*, double);// 在设定的时间执行事件Scheduler();virtual ~Scheduler();int command(int argc, const char*const* argv);double clock_;     //初始化为0int halted_;       //初始化为0static Scheduler* instance_;static scheduler_uid_t uid_;};


(2)通过一个实例来分析调度器的相关代码以及调度的步骤,实例如下:

Scheduler &s=Scheduler::instance();    //创建一个调度器s.schedule(&sleep_handler,&sleep_event,duration_);    //调度:在当前时间之后的duration_秒后调用sleep_event事件的sleep_handler

1、调度器:schedule( )

void Scheduler::schedule(Handler* h, Event* e, double delay){// handler should ALWAYS be set... if it's not, it's a bug in the callerif (!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_++;            //赋给此调度事件一个id号,同时更新调度队列存储的事件数e->handler_ = h;            double t = clock_ + delay;  //保存触发事件的时间:当前时间+要延时执行的时间e->time_ = t;               //指定时间的触发事件insert(e);                  //将此调度事件插入调度队列中}

取最简单的链表调度器来分析:其过程是取队首地址&queue_,从队首开始判断已经在队列中的每一个事件的执行时刻与要插入队列的事件e的执行时刻的关系,按照执行时刻的先后顺序将事件e插入到队列中。

2、insert( )函数

void ListScheduler::insert( Event* e,){    double t=e->time_;     Event **p;      /p是一个二级指针     for(p=&queue_;*p!=0;p=&(*p)->next_){   //for循环判断应当将该事件差入到队列的位置        if(t<(*p)->time_)           break;     }     e->next_ = *p;    //将事件e插入调度队列中     *p = e;            }

最初的调度队列是一个空队列,不断有事件被插入队列,run( )函数是执行调度器队列中事件的函数,NS中的调度机制负责调用run( )函数处理队列中的事件,以使队列中的事件不会累积。

3、run( )函数

void Scheduler::run(){instance_ = this;Event *p;/* * The order is significant: if halted_ is checked later, * event p could be lost when the simulator resumes. * Patch by Thomas Kaemer <Thomas.Kaemer@eas.iis.fhg.de>. */while (!halted_ && (p = deque())) {     //deque()函数取出队首事件,如果时间不为空,即不是null,dispatch(p, p->time_);         //则调用dispatch()来处理事件}}
4、deque()函数将队首事件取出并将下一事件作为新的队首事件:
Event* ListScheduler::deque( ){         Event * e=queue_;      /p是一个二级指针     if(e)       queue_=e->next_;    //如果调度队列中仍有等待调度的事件,队首就不为空,将队首取出,同时,设置下一事件为队首     return(e);          }
5、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_; // 如果时间已经被dispatch()处理,将事件id标记为负p->handler_->handle(p); // 调用事件的handler()函数}

6、执行handler()函数:
class SleepHandler: public Handler{ public:  SleepHandler(RMac* p): mac_(p){};  void handle(Event*); private:  RMac* mac_;};
void SleepHandler::handle(Event* e){  mac_->ProcessSleep();}
 
阅读全文
0 0
原创粉丝点击