到子类MyCommand的匹配,并调用MyCommand::command()。其流程图如图2.5所示:若图片无法显示请联系QQ752018766
图2.5
用户对脚本tclcl/tcl-object.tcl进行修改,或是修改、增加tcl/lib的文件来对ns进行扩展。对于新文件的装载是由类EmbeddedTcl的对象来完成的。
Tcl脚本其实就是由char类型数据组成的文本文件,所以类Embedded的构造函数可以用char*型指针指向脚本代码,并将此指针值赋与成员变量 code_。EmbeddedTcl的定义中(tclcl/tclcl.h)有一load()成员函数。Load()用Tcl::instance()获得解释器句柄,然后调用Tcl::evalc,调用Tcl命令eval,完成对代码的装载:
void EmbeddedTcl::load() { Tcl::instance().evalc(code_); }
(6) InstVar类:
类InstVar定义了显式实现绑定机制的方法。
绑定过程bind()通常需要指定解释变量名和编译对象的成员变量的地址。基类InstVar的构造函数在解释器里创建一个实例变量,建立陷阱例程(trap routine),捕捉所有通过解释器对变量的访问。当解释器有对变量的读操作发生时,触发陷阱例程,陷阱例程调用适当的get函数,取对应的编译对象的成员变量的当前值,并给解释变量赋值。对写操作,只是触发时间略有不同,在解释器写完解释变量之后,触发陷阱例程,陷阱例程调用适当的set函数,将改变后的值写回编译对象。
类InstVar有一个成员变量tracedvar_指向类TracedVar对象,TracedVar对象封装了有关编译对象方面的信息和方法,定义如下(~/ns/tclcl/tracedvar.h):
class TracedVar {
public:
TracedVar();
virtual char* value(char* buf, int buflen) = 0;
inline const char* name() { return (name_); } //取变量名
inline void name(const char* name) { name_ = name; } //置变量名
inline TclObject* owner() { return owner_; } //返回owner_
inline void owner(TclObject* o) { owner_ = o; } //置owner_
inline TclObject* tracer() { return tracer_; } //返回tracer_
inline void tracer(TclObject* o) { tracer_ = o; } //置tracer_protected:
TracedVar(const char* name);
const char* name_; //变量名
TclObject* owner_; //拥有该变量的对象指针
TclObject* tracer_; //当变量改变时回叫tracer_public:
TracedVar* next_; //指向下一个TracedVar变量,形成队列
};
从基类InstVar又派生了五个子类:class InstVarReal、class InstVarTime、class InstVarBandwidth、class InstVarInt和class InstVarBool。分别用来绑定real型、time型、bandwidth型、integer型和boolean型变量。
NS对网络实体的仿真和各种功能模块都封装在派生类中。
首先澄清两个问题:
(1)由于NS是由C++代码与Otcl代码组成的,其派生类的构成也因此很复杂,会由相应的解释类与编译类绑定而成。解释类基本上是与编译类是相对应的,这里的对应,指的是名字相似的C++类与Otcl类一般实现对同一实体的仿真。但两者的作用是不一样的,一般说来,C++类实现底层的计算和操作,比如说路由计算、数据包的接收丢弃等等。Otcl类在高层配置节点和拓扑图,提供用户接口,调用C++代码完成实际工作。值得注意的是,许多复杂的解释类在C++代码里没有相应的编译类,比如说以下提到的类simulator,它由node、link等成员类组成,在C++里就没有相应的类。
(2)C++代码中的派生类大多数为TclObject和TclClass派生类。如前述,TclObject的派生类与TclClass的派生类几乎是一一对应的,每一TclClass派生类有一create()函数创建对应的TclObject对象。但两类之间有很多不同。网络中各实体、功能、结构大不一样,且本身就存在许多层次和组合。所以模拟网络实体的TclObject系列,结构都比较复杂,经常由几个相关类又组合成一个新的类,甚至会有多继承现象发生。而TclClass系列则简单得多,几乎所有的子类都不再有派生类,也就是说几乎所有的子类都直接从基类TclClass派生,只有一个特殊情况,就是它的子类PackHeaderClass派生出许多子类,比如ARPHeaderClass、encapHeaderClass等等。
1、仿真器类
整个仿真器是由一个Tcl的仿真器类(class Simulator)来描述的,它提供了一个接口的集合。这个接口集合被用于设置一个仿真过程和选择用于驱动仿真事件的调度程序类型。一个仿真的脚本文件一般情况下开始于建立这种类的实例,并且应用不同方法来建立结点拓扑结构和配置仿真的其它部分。
仿真器类是一个解释类,没有相应的编译类。但仿真器类是由许多更小的类构成的,这些类有相应的编译类。从ns外部看来,整个的仿真过程可以看成对仿真器的操作。因此,我们的工作从创建一个仿真器的实例对象开始,之后,通过这个仿真器调用各种方法生成节点,进而构造拓扑图,对仿真的各个方面进行配置,定义事件,然后,根据定义的事件,模拟整个网络活动的过程。
有关仿真器的文件有ns/tcl/lib/ns-lib.tcl,ns/scheduler.{cc,h},和 ns/heap.h。相对于C++代码中类的构造函数,OTcl代码有类的初始化函数,仿真器类的初始化函数如下:
Simulator instproc init args {
$self create_packetformat
$self use-scheduler Calendar
$self set nullAgent_ [new Agent/Null]
$self set-address-format def
eval $self next $args
}
在解释器中创建一个新的仿真器对象,初始化函数执行以下操作:
Ø 初始化数据包格式(又称create_packetformat)
Ø 创建一个调度员(scheduler)
调度员以事件驱动的方式控制仿真的运行,缺省的调度员为calendar scheduler。当然,用户可以指定别的调度员,以实现别的调度策略。目前可用的调度员有四种:Sceduler/Link,Sceduler/Heap,Sceduler/Calendar queue和Sceduler/RealTime。
Ø 创建一个“null agent”
语句set nullAgent_ [new Agent/Null]可以创建一个null agent,用来发送、接收一个数据包。
总的说来,仿真器提供了一系列的方法,可以分三类:创建、管理拓扑图(通过管理节点,和链来实现),跟踪,协调函数与调度。
仿真器封装了许多功能模块,最基本的是节点、链路、代理、数据包格式等等。下面分别是这些模块的解释。
2、 事件调度器和事件
(1)事件调度器(event scheduler)
NS仿真器是一个以事件来驱使的仿真器,目前NS支持2种类型的事件调度器:非实时的(none real-time)和实时的(real-time)。非实时的调度器又分为3种:linked-list(链表)、heap(堆栈)、calendar (队列,这是缺省的scheduler);从逻辑上说这3种scheduler是相同的,但他们采用的数据结构不同。之所以要保留3种scheduler,主要是为了使NS能够向前兼容,因此我们在使用非实时的scheduler时,一般只需要用缺省的calendar
上一页 [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] ... 下一页 >>