转载地址:http://hi.baidu.com/aoxinguy/item/0b19374570056be2bcf4517a
从仿真原理上来说,因为NS的仿真都是单线程的,所以,Simulator按理来说只能有一个。
因此,我们在脚本中 Set ns [new Simulator] 一般只出现一次。 但是,NS2的仿真代码中,并没有预料到用户新建两个Simulator的实例的情况。 不管是在 OTCL的 Simulator类 还是 C++的 Simulator类,都没有真正地实现SINGLETON的设计模式,来预防用户错误地建立多个Simulator实例的情况。
在C++层次中,Simulator类有一个静态成员 Simulator* instance,按理说,为了真正实现 SINGLETON 模式,最简单的方法是将Simulator的构造函数保护起来,然后再通过一个Instance过程去访问这个静态成员。通过判断来保持实例的唯一性。如果instance为零(),则调用构造函数新建一个实例;否则直接返回当前实例值。 但是,这样做的问题是:tclclass中 的 new Simulator()就无法调用了(因为此时Simulator的构造函数是受保护的了),这样,就使得OTCL与C++的绑定机制出现了例外。
NS 中 权衡解决了这个问题。即将 Simulator()的构造函数公有化,从而允许在Tclclass中动态创建Simulator实例。 但,它却无法保证 Simulator实例的唯一性,而只能保证Simulator的静态成员 instance始终指向当前调用的Simulator实例(在 Command函数中实现)。 也就是说,可以在C++层次或者说OTCL层次(通过 new Simulator) 来多次创建Simulator的实例。 比方说,我们可以修改main函数,在其中定义多个Simulator实例(当然,在OTCL中你是无法操纵这些实例的)。 在C++中直接定义Simulator对象,是不会造成内存泄露的;因为在退出main函数时,先前构造的对象会自动解析。但,在NS2中,一般C++对象都是通过OTC调用 new Class() 创建
{不清楚,ns2如何释放这些动态创建的对象?应该在框架中的某个地方有相应的代码;在tclclass的 create-shadow()中,如果通过调用 TclObject* o = p->create(argc, argv); 成功创建并获得C++对象的指针;则会有一个 tcl->enter(o) 调用。 将动态创建的C++对象地址存储到Hash表中。据此猜测,NS在结束(正常结束时)应该会遍历这个hash表中存储的C++对象,并通过delete o 一一将它们释放 从这个角度来看,应该也不会存在C++对象动态创建以后,引起的内存泄漏问题; 除非 NS2遇到什么问题非正常结束? 从而未来得及调用 相应的 hash操作 释放内存 }
晕,讨论到 Simulator多个实例创建时可能出现的 内存泄漏问题了~ 偏题了。 NS的框架应该比较完善,就算是非正常退出,应该出会来得及释放hash表中存储的 C++实例所占用的内存(父类均为 TclObject). 以后有空再确定Hash实例相应的内存释放代码后,再来完善这部分说明吧~
转到 讨论 在 OTCL层次上,新建几个 OTCL Simulator实例会引发什么问题上。
我对 贴中的脚本稍做修改,添加了一行 Set ns2 [new Simulator],结果,发现,脚本非正常退出了。
通过单步跟踪,我发现,原来是在 创建节点时, $ns node 最终会调用到 node 的 init过程。在这个过程中,会通过set ns_ [info Simulator instances] 获得Simulator的所有实例。
在这个脚本中,因为,我定义了两个 Simulator,所以,此处 ns_ 应该会是 两个实例名字组成的数组,实际上是 ( _o11 _o4 )。 其中 _o11对应$ns 实例,_o4对应 $ns2实例。
当接下来,执行到这句: set nodetype_ [$ns_ get-nodetype] 时, 因为 $ns_ 此时,已经是两个实例名的联合, 所以,在调用 get-nodetype时,程序会发现,调用者不再是一个对象名,而是两个对象名联合,故无法识别,从而错误退出,错误信息如下:
invalid command name "_o11 _o4"
while executing
"$ns_ get-nodetype"
(procedure "_o18" line 22)
(Node init line 22)
invoked from within
"_o18 init "
(Class create line 1)
invoked from within
"Node create _o18 "
invoked from within
"if [catch "$className create $o $args" msg] {
if [string match "__FAILED_SHADOW_OBJECT_" $msg] {
delete $o
return ""
}
global errorInfo
error "class $..."
(procedure "new" line 3)
invoked from within
"new Node"
("eval" body line 1)
invoked from within
"eval new [Simulator set node_factory_] $args"
(procedure "_o4" line 32)
(Simulator node line 32)
invoked from within
"$ns node"
invoked from within
"set node1 [$ns node]"
(file "test.tcl" line 29)
1: exit 1
到这里,我们得到的结论是:在仿真脚本中,不能定义多个 Simulator实例(暂且抛开会不会引起内存泄漏的问题不说,脚本本身就无法成功执行的)
但是,我自己认为; NS2完全可以在OTCL脚本层次 来实现 在C++中,因为权衡而无法实现的SINGLETON模式,保证在OTCL类中,有且只有一个Simulator实例。
其实现可放在Simulator类的 init 过程的最末尾(在eval $self next $args后),原理大致如下:
eval $self next $args最终会调用 create-shadow函数创建相应的 Simulator C++实例。在这句执行之后,可添加 info Simulator instance来判断当前Simulator OTCL实例是否唯一。如果不唯一的话,则除保留一个实例外,其它的所有实例均先将其关联的C++实例释放掉,而后再将本身的其它所有实例均指向仅剩的唯一实例即可。
这样一来,可以保证用户即使在脚本中多次创建OTCL Simulator实例,实际上只有一个实例且所有定义的实例变量均指向该唯一实例。