elenna's profile๑۩ﺴ 猪猪艾美丽的学习乐园BlogListsGuestbook Tools Help

Blog


    March 11

    grub4dos---加载双系统中的linux!

    1.进windows,下载、解压grub for dos。

    2.安装grub4dos。复制grldr到c:\,修改boot.ini文件,在最后一行添加c:\grub="GRUB"。引号里面的内容可以随便写,它的作用主要是开机后windows引导菜单里显示的内容。如果看不到boot.ini文件,去掉查看->文件夹选项->隐藏系统保护文件和选中“显示所有文件”。

    3.重启系统,在windows的启动引导菜单里,选择“GRUB”,系统完全交由grub控制。grub的功能还是很强的,可以直接从FAT、minix、FFS、ext2或ReiserFS分区读取Linux内核。这就意味着无论怎样它总能找到内核。这时会出现一个grub>提示符(进入grub后选择command line),其实它引导内核的步骤跟GNU grub是一样的。

    4.手动加载Linux(所有命令均在grub>提示符下输入,使用tab键自动填补命令)

    help命令可用于查看grub的所有命令格式。

    cat (hd[0-n],y)/etc/fstab用于查看分区情况,以找到linux所在分区即分区类型为ext2。输入(hd0,2)意味着linux在第一个硬盘的第三个分区上。

    首先键入root(hd0,2)用于设置包含操作系统内核的根设备。

    然后键入kernel /boot/vmlinuz-xxx ro root=/dev/hda3(或root=LABEL=/)。kernel命令用于装载内核映像文件,vmlinuz-xxx是你内核文件的名字。后面的root=/dev/hda3表示内核文件所在的磁盘分区,ro表示以只读方式打开内核文件。

    然后键入initrd /boot/initrd.img-xxx。initrd.img-xxx是一个镜像文件,类似ramdisk,把一段程序打包到img里。它的作用是在没有mount /分区以前,虚拟出一个/,然后执行其根目录下的一个脚本"linuxrc",做一些挂载scsi驱动类似的事情,如果你的磁盘并不是scsi磁盘的话,这步并不是必须的。

    最后键入boot,引导进入linux。

    5.将新的grub装入mbr。以root的身份登录,运行grub-install /dev/hda3,或者键入grub,然后在提示符下键入root (hd0,2),setup (hd0),把mbr的控制权抢回来了!

    6.将windows加入grub启动菜单。root权限,修改/boot/grub/menu.lst。加入以下内容:

    title Microsoft Windows XP Professional
    root (hd0,0)
    savedefault
    makeactive
    chainloader +1

    参考:http://hi.baidu.com/opthome/blog/item/09ef04124bd18e55f919b80c.html

    grub命令行用法 http://hi.baidu.com/realxfliu/blog/item/f6fe14e96463b83fb90e2d35.html

     

    PS:另一种方案---windows下安装grubfordos后, 拷贝原linux系统/boot/grub/menu.lst到c:\下(需要在重新安装windows前保存)。但我采用这种方案时,在kernel、initrd后的savedefault命令出错,估计要修改menu.1st,去掉savedefault命令。

    August 11

    《NS与网络模拟》学习13——完整例子

           通过MFlood协议的添加,可以总结出以下的大概流程:

    1.添加协议类:

       1)添加自己的Agent类,重载recv()、command()等函数

       2)创建对应的TclClass类

       3)当添加的是无线自组网下的路由协议时,需要修改NS的系统Tcl代码,使得当设定路由协议时会调用我们新增加的Agent/NewAgent类。(~ns/tcl/lib/ns-lib.tcl)

    2.增加包头类型:

       1)定义自己的hdr_newhdr结构

       2)通过PacketHeaderClass类绑定

       3)修改~ns/tcl/lib/ns-packet.tcl使得新加入的包头类型被激活

       4)如果想要创建新的包类型,修改~ns/common/packet.h,将名字与包类型绑定

    3.修改~ns/Makefile文件,增加对新类的编译。编译代码->调试。。。

    4.初步测试:编写简单的场景文件,研究trace文件

    5.可以利用setdest和cbrgen工具编写复杂的测试场景文件,并多次测试;应用gawk等对trace文件进行分析;利用画图软件绘测出所需图像;利用nam查看运行过程。

    《NS与网络模拟》学习12——ns中的小工具

    一.cbrgen(~ns/indep-utils/cmu-scen-gen)

       cbrgen用来生成传输负载,可以产生TCP流或者CBR流。 使用方法如下:

       ns cbrgen.tcl [-type cbr|tcp] [-nn nodes] [-seed seed] [-mc connections] [-rate rate]

                          -type tcp流或cbr流              -nn 节点数  
                          -mc 节点间的最大连接数          -rate  每个连接间的流的负载数
                          -seed  指定随机数种子

    二.setdest(~ns/indep-utils/cmu-scen-gen/setdest/)

       用来随机生成无线网所需的节点运动场景,使用前需make。使用方法如下:

       ./setdest   -v <1> -n <nodes> -p <pause time> -M <max speed> 
                       -t <simulation time> -x <max X> -y <max Y>

        或:./setdest   -v <2> -n <nodes> -s <speed type> -m <min speed> -M <max speed>
                             -t <simulation time> -P <pause type> -p <pause time> -x <max X> -y <max Y>

        其中,speed type为uniform/normal,pause type为constant/uniform

        以上两个工具产生的文件均可采用source命令加入到tcl文件中,注意产生的文件用到了诸如node_数组变量和god_变量,需要在tcl脚本中预先建立。

    三.threshold工具(~ns/indep-utils/propagation)

        用来在无线网络中,计算在某种传播模型下,如何设定接收功率阈值来控制无线传输的范围。

        先编译得到可执行文件:g++ threshold.cc -o threshold

        命令如下:threshold -m <propagation-model> [other-options] distance

    common parameters: -Pt <transmit-power> -fr <frequency> -Gt <transmit-antenna-gain>
                                 -Gr <receive-antenna-gain> -L <system-loss>
    for two-ray ground model: -ht <transmit-antenna-height> -hr <receive-antenna-height>
    for shadowing model: -pl <path-loss-exponent> -std <shadowing-deviation> -d0 <reference-distance> -r <receiving-rate>

    四.nam(Network Animater)

        nam的功能是根据网络模拟软件或真实环境里的特定格式的trace输出文件来运行动画,例如,trace文件常常来自NS模拟器或者Tcpdump软件的输出。

        NS中控制nam动画显示的命令如下:

    1.节点:$node color [color] ;#设定节点的颜色
              $node shape [shape] ;#设定节点的形状
              $node label [label] ;#设定节点的名称
              $node label-color [lcolor] ;#设定节点显示名称的颜色
              $node label-at [ldirection] ;#设定节点名称的显示位置
              $node add-mark [name] [color] [shape] ;#增加注释
              $node delete-mark [name]              ;#删除注释

    2.链路和队列:$ns duplex-link <attribute> <value>
                            attribute:orient、color、queuePos、label

    3.Agent:

    使用下面的命令就可以使想要显示的Agent以AgentName出现在节点附件的方框内:$ns add-agent-trace $Agent AgentName

    《NS与网络模拟》学习11——gawk

           gawk是一种程序语言,对于资料的处理具有很强的功能,可以使用很短的代码轻易地完成对文本档案做修改、分析、提取和比较等处理。在NS的模拟结果分析中,经常用到gawk来进行数据分析和统计等。命令如下:

           gawk 'program' input-file1 input-file2 ...                                 程序代码较短
           gawk -f program-file input-file1 input-file2...                           程序较长

           gawk程序是由很多的pattern和action所组成的:pattern   {action}。功能是针对文件的每一行搜寻指定的模式(pattern),当一行里有符合指定的模式时,gawk就会在此一行执行指定的动作(action)。

           一些系统变量:RS——记录分割符(默认:\n)    FNR——目前的输入档案已经被读取的记录个数
                             NR——所有输入档案已经被读取的记录个数
                             FS——栏分隔符(默认:空白字符) NF——目前记录有多少个栏位
                             OFS——输出栏位分隔符(初始值:空格) ORS——输出记录分隔符(初始值:\n)

    Pattern包括:

           /regular expression/——正则表达式,如exp~/regexp/,exp!~/regexp/

           /expression/——单一表达式,值不为0或字串不空时视为匹配

           pat1,pat2——指定记录的范围

           BEGIN、END——gawk在开始执行或要结束时会分别执行BEGIN或END指定的action

           null——对于每个输入记录皆视为符合Pattern

    Action包括:

           Expression——算术运算、比较运算、布尔运算、条件运算等

           控制语句——if、while、do-while、for、break、continue、next(next file)、exit

           内建函数——数值方面的函数(sqrt、exp、log、sin、rand、srand)

                           字串方面的函数(index、length、match、sprintf、sub、gsub、substr、tolower、toupper)

                           输入输出的内建函数(close、system)

            使用者定义的函数

    August 10

    《NS与网络模拟》学习9——代理和应用

    一.Agent     

       agent代表了网络层分组的起点和终点,创建新型Agent:

          1)确定继承结构
          2)定义recv()方法和timeout()方法
          3)构造所需要的定时器
          4)定义OTCL Linkage函数
          5)写OTCL代码访问该agent

          比较熟知的两个agent协议分别是UDP和TCP,介绍见书。

    二.应用层

          应用层程序建立在运输层代理(transport agent)之上,分为两大类:流量发生器(traffic generator)和应用模拟器(simulated application)。流量发生器一般用在UDP代理之上,应用模拟器一般用在TCP代理之上。

          运输层代理的API函数:NS通过一系列预先定义的API函数来模仿sockets API的功能。运输层代理和应用层程序是如何通过API通信并相互配合进行工作的?

         1)将运输层代理绑定到节点上,如:
           set src [new Agent/TCP/FullTcp]
           set sink [new Agent/TCP/FullTcp]
           $ns attach-agent $node_(s1) $src
           $ns attach-agent $node_(k1) $sink
           $ns connect $src $sink
         2)将应用层程序连接到运输层代理上,如:
           set ftp1 [new Application/FTP]
           $ftp1 attach-agent $src
         3)通过系统调用使用运输层代理
            这些调用可以在Otcl或C++中执行,包括:send()、sendmsg()、closse()、listen()、set_pketype
         4)运输层代理对应用程序的回调,包括:recv()、resume()等

         四个主要的流量发生器为(Application/Traffic/~):Exponential(按照指数ON/OFF分布产生数据)、Pareto(按照Parento ON/OFF分布产生数据)、CBR(按照一个确定的速率产生数据)、Trace(按照一个trace文件产生数据)。Traffic/Trace类使用attach-tracefile方法将一个Traffic/Trace对象和一个特定的Tracefile对象结合起来。

         应用模拟器包括:Applicaiton/FTP(attach-agent、start、stop、produce、producemore、send n)
                              Application/Telnet

    《NS与网络模拟》学习10——trace

         trace可以根据用户的需要记录模拟过程中的任何一个细节。为此,每个分组都包含一个特殊的common分组头,包含了分组的序列号、分组的类型值、分组的大小和端口标识等。

         Tcl中使用Trace类的命令包括:

         flush-trace{}——刷新模拟过程中所有Trace对象的缓冲区
         create-trace {type file src dst}——在给定的src节点和dst节点之间创建一个类型为type的Trace对象。
         trace-queue {n1 n2 file}——设定节点n1和n2之间链路的trace,使能了Enque、Deque和Drop的trace。
         trace-callback {ns command}——每当新增一行trace时调用command
         drop-trace {n1 n2 trace}——设置trace对象为n1和n2链路队列的drop-target

         C++的Trace类用来实现Otcl中的Trace/Hop、Trace/Enque、Trace/Deque、Trace/Drop类的功能。Trace::format()方法定义了由Trace产生的trace文件的格式。

         有线模拟和无线模拟的trace文件格式参考manual手册。

    《NS与网络模拟》学习8——分组头管理

         分组(Packet)是对象间交互的基本单元,由一系列分组头和一个可选的数据空间组成。分组头的结构在Simulator对象创建时就被初始化了,同时每个分组头相对于分组的起始地址的偏移量也被记录下来。

         用户可以为新的协议定义该协议自己的分组头,也可以通过增加域的方式扩展现有的分组头。假如我们想要增加一个叫newhdr的新的分组头,需要完成以下几个步骤:

    1)在C++中创建一个名为hdr_newhdr的新的structure来定义所需要的字段,定义offset_字段和访问字段的方法。
    2)定义所需要的访问其他字段的成员函数
    3)创建一个叫做PacketHeader/Newhdr的static类来完成Otcl连接,在它的构造函数里进行bind_offset()。(PacketHeaderClass)
    4)编辑~ns/tcl/lib/ns-packet.tcl来激活新的分组头(PacketHeaderManager)。另外也需修改~/ns/common/packet.h,来绑定分组类型值和它们的名字(p_info)。

         与分组相关的类概括起来有四个:Packet、p_info、PacketHeaderClass、PacketHeaderManager

    1.Packet类定义了分组的结构(bits_,hdrlen_等),提供了处理Packet对象的一系列成员函数(alloc()、copy()、free()等)。

    2.p_info类,用来绑定各个分组类型值和它们的名字。

      当定义新的分组类型后,数字代码应添加到枚举类型packet_t中,同时应添加到p_info类的构造函数中。注意,PT_NTYPE必须是最后一个。

    3.PacketHeaderClass是各种分组头的基类。其中,hdrlen_是在构造函数中被设置,offset_则是通过调用bind_offsest()函数来设置的。

    4.PacketHeaderManager类用来管理所有处于激活状态的分组头,并在BOB中分配给它们唯一的偏移量。同时定义于C++和Otcl中。

       模拟过程初始化时,ns-packet.tcl代码被执行。foreach循环中所有需要激活的分组头,通过调用add-packet-header来激活(Common头始终被激活)。create_packet_format{}被调用一次,它首先创建一个PacketHeaderManager对象,之后,对于被激活的分组头,由实例过程allochdr{}来给出分组头的位置。

       在Tcl中选择分组头的命令包括:remove-packet-header、remove-all-packet-header和add-packet-header。remove-packet-header必须在Simlator对象创建前执行。

    《NS与网络模拟》学习7——定时器

           定时器(timer)既可以载C++中实现也可以在Otcl中实现。

    1.C++中

       在C++中,各种定时器都是基于抽象基类TimerHandler的,经常用于agent对象中,但也可以被其他对象使用。这里介绍在agent中使用timer的方法:

    1)定义新的定时器

    class MyTimer:public TimerHandler {
    public:
        MyTimer(MyAgentClass *a):TimerHandler() {a_=a;}
        virtual double expire(Event* e);
    protected:
        MyAgentClass* a_;
    }

    2)在agent初始化函数中,调用MyTimer(this)

    3)定义纯虚函数expire()

    MyTimer::expire(Event* e) {
       //do the work
       //return TIMER_HANDLED;            //=>do not reschedule timer
       //return delay;                    //=>reschedule timer after delay            
    }

    2.Otcl Timer类

       定义的一些实例方法包括:sched、cancel、resched、expire等,Timer类没有定义timeout过程,需要派生类自己定义这个过程来完成定时器超时后的相关处理。

    《NS与网络模拟》学习6——链路

           创建单向链路的命令为:$ns simple-link <node0> <node1> <bandwidth> <delay> <queue_type>

           单向链路的结构示意图如下:

                                                     image

          SimpleLink类中有5个主要的实例变量,enqT_、deqT_、recvT_和drpT_等是与trace相关的对象

         1)head_link:指向link的第一个对象。
         2)queue_:指向link的主队列元素。
         3)link_:指向完成延迟和带宽计算的对象。
         4)ttl_:指向完成TTL计算的对象。
         5)drophead_:指向完成对分组丢弃功能的对象。

         与链路有关的queue、DelayLink和TTLChecker均为Connector类的子类,一般都通过重载Connector类的recv()函数,来完成自己独特的功能。Connector类与Classifier类不同:收到一个分组,对分组做相应的处理,然后将分组递交给target_对象,或者将分组丢弃(递交给drop_对象)。

    《NS与网络模拟》学习5——节点

    一. 节点

     1.创建节点——$ns node

        单播节点的结构                                                                         组播节点的结构

        image            image  

    2. 配置节点——$ns node-config

       配置的各项参数见书。注意的是,当配置无线节点时,不需要设置-addressType,并且-channelType的设置也有所不同。

    3.分类器(classifier)

        NS中有各种不同的Classifier对象,分别负责检查分组的不同部分,来完成不同的匹配、筛选工作。每个classifier都包括一个由slot number作索引的对象表。classifier的工作是检查收到的分组的slot number,然后把分组转发给由该slot number索引的对象。介绍几种主要的Classifier类:

    1)address classifier:按照分组的目的地址进行匹配,对分组的目的地址做位运算来产生一个slot number

    2)port classifier:按照分组的目的端口进行匹配,将分组传递给相应的Agent对象。dmux_是节点的PortClassifier对象。

    3)replicatior:生成一个分组的多份拷贝,并把这些拷贝转发给slot表中的所有对象。

    4.Tcl中相关命令包括:

       routing-->add-route/delete-route
       transport-->attach/detach
       Classifier-->insert-entry/install-entry/install-demux

    二.移动节点

         NS的无线模块是由CMU的Monarch工作组引入到NS中的,以MobileNode为基本核心。MobileNode是Node类的派生类,它的功能(包括节点移动、周期性的位置更新、维护拓扑边界等)是在C++中实现的,而设定MobileNode的各个网络构件则是在Otcl中实现的。

         同样用node-config函数来配置一个移动节点。移动节点的各个网络构件如下:

                                                             image

    1)Link Layer:对于所有发出的分组,路由Agent会把分组传递给LL,LL把分组传递给接口队列(IFQ)。对于所有接收到的分组,MAC层将分组传递给LL,LL再将分组传递给node_entry_。移动节点的LL还连接了一个ARP模块,用来把IP地址解析成物理(MAC)地址。

    2)ARP:如果ARP已经知道目标节点的MAC地址,直接写入分组的MAC头中;否则,存放到分组缓冲区,并广播ARP请求。

    3)InterfaceQueue:由PriQueue类实现。是一个优先级队列,优先处理路由协议分组,并可以对分组进行过虑。

    4)MAC层:实现了IEEE802.11的DCF MAC协议。

    5)Network Interface:网络接口是移动节点访问信道的接口,通过碰撞和无线传输模块来接收其他节点发送到信道上的分组。将波长、传输功率等信息写入分组头。

    6)Antenna:使用单一增益的全向天线。

    7)Radio Propagation Model:用来计算每个分组在到达接收节点时的信号强度,小于阈值时标记为error并被MAC层丢弃。包含3种模型:Free-space、Two-ray ground reflection和Shadowing。

    8)Channel:无线信道的功能是将分组复制给所有连接到本信道上的移动节点。

        移动节点运动的方法:1.指定节点的起始位置和终止位置,通常放在一个单独的场景文件中;2.节点随机运动:$mobilenode start使得节点从随机位置开始随机运动,目标和速度是随机产生的。无论采用哪种方式,都需在创建移动节点之前定义移动节点的移动范围,用下面的办法定义平面拓扑的长和宽:

             set topo [new Topography]
             $topo load_flatgrid $opt(x) $opt(y)     

    《NS与网络模拟》学习4——NS的事件调度机制

         在NS中,整个模拟过程由一个名位Simulator的Tcl类来定义和控制。Simulator提供了一些与建立模拟有关的方法,分为3类:(1)创建和管理拓扑结构(即管理node和link);(2)与tracing有关的方法;(3)与事件调度器有关的方法。

         Simulator对象的一系列初始化工作包括:(1)通过调用create_packetformat来初始化packet的结构;(2)创建一个“事件调度器scheduler”;(3)创建一个“null agent”。

         NS是一个事件驱动的模拟器。支持2种类型的事件调度器:非实时(linked-list、heap、calendar缺省)和实时的。scheduler的主要功能是处理分组的延迟和充当定时器。执行过程:从所有事件中选择发生时刻最早的事件,调用它的handler函数,把该事件执行完毕,然后从剩余的所有事件中选择发生时刻最早的事件,如此反复执行。

         一个事件(event)通常由触发时间和Handler函数组成,有两个派生类:at-events和packets。相关的Tcl命令包括:

                set ns [new Simulator]
                $ns halt
                $ns run
                $ns at <time> <event>

    August 09

    《NS与网路模拟》学习3——Tcl类和EmbeddedTcl类

    1.Tcl类

       类Tcl封装了Otcl解释器的实例。

    1)Tcl实例获取方法:Tcl& tcl=Tcl::instance();

    2)c++调用Otcl过程:通过tcl.eval()等。

    3)返回值:tcl.result(void)来获取执行结果。

    4)对象查找:NS使用TclObject的名字作为健值在哈希表中插入、查找或删除TclObject。用户可能用到的是查找:

       TclObject* Tcl::lookup(const char* s)

    2.如何修改NS各种构件的Otcl代码以及很多初始配置脚本?

      先来看一下NS如何通过EmbeddedTcl类将一些初始Otcl脚本载入NS的:EmbeddedTcl类的load()方法将构造函数中传入的code_字符串作为Otcl命令执行了;而ns_tcl.cc中包含两个语句,第一个定义巨大字符常量code,第二,创建EmbeddedTcl对象et_ns_lib;而ns_tcl.cc是在编译NS的过程中自动生成的。如下:

      Makefile文件中:tclsh bin/tcl-expand.tcl tcl/lib/ns-lib.tcl |tcl2c++ et_ns_lib > gen/ns_tcl.cc。

    1)用tclsh来运行tcl-expand.tcl,将tcl/lib/ns-lib.tcl中的所有source语句行用所对应的Tcl脚本替换掉。

    2)把所得文本通过管道传送给tcl2c++程序。

    3)tcl2c++用扩展后的Tcl脚本作为字符串常量code,构造了EmbeddedTcl对象et_ns_lib

    4)最终将生成的C++程序重定向为gen/ns_tcl.cc

      因此,扩展NS的Otcl脚本不仅把它放在~ns/tcl/目录下,需要在tcl/lib/ns-lib.tcl中加一条source命令,还需重新编译NS,以便重新生成ns_tcl.cc。

    《NS与网络模拟》学习2——分裂对象模型和TclCL

         NS利用TclCL建立起分裂对象模型,形成了其丰富的构件库。C++是强制类型语言,适合用于具体协议的实现,而Otcl不是强制类型的,适合用来做模拟配置。所有C++类(编译类)都是从类TclObject一级级继承出来的,而所有Otcl类(解释类)都是从SplitObject一级级继承出来的。

         每个编译对象都是当用户从解释器中创建解释对象的同时再C++类结构中产生出的影像对象。而类TclClass则包含了执行这种映像的机制。

    1. 通过构建解释对象来构建其影像对象——编译对象(反之不行)

         Otcl脚本中使用过程new{}和delete{}来完成。具体如下:调用new{}创建解释对象->调用初始化实例过程init{}->调用父类init{}->最终调用基类SplitObject的init{}->create-shadow创建对应的编译对象->构造函数被执行,进行变量绑定等工作。

         可见每个编译类都必须在初始化实例过程中调用其父类的初始化实例过程,并且最好先于当前类的初始化,以便绑定变量能够尽早被创建;对象的销毁过程不许在其最后一条语句显示调用父类的销毁过程(最终,调用delete-shadow,导致影像对象被析构)。但一般来说,如果没有其他特殊的事情要做,初始化实例过程可以省去。

    2.create-shadow如何知道创建编译对象所属类?

        通过C++类TclClass来解决。如

    static class RenoTcpClass:public TclClass {
    public:
        RenoTcpClass():TclClass("Agent/TCP/Reno") {}                        //调用基类构造函数,登记申明所属Otcl类 Agent/TCL/Reno
        TclObject* create(int argc,const char*const* argv) {         //每当创建解释对象Agent/TCL/Reno时,creat-shadow就会来调用RenoTcpClass的
            return (new RenoTcpAgent());                                           //create函数,创建出正确的影像对象,并返回了其指针
            }
    }

    3.在解释对象的成员变量和编译对象的成员变量间建立双向的连接(不适用于静态成员变量的绑定)

         变量绑定——通常在编译对象初始化时在编译对象的构造函数中建立,在解释对象中作为实例变量来访问。大多数绑定变量的初始值在~ns/tcl/lib/ns-default.tcl中定义。

    4.在Otcl对象中调用对应的C++对象的方法

         对于每个TclObject,ns为其Otcl中的解释对象建立一个实例过程cmd{},cmd{}根据保存的编译对象的指针调用其方法command(),并将cmd的参数作为一个参数数组传递给command。两种方法调用cmd{}:显式和隐式,如:

          $mobilenode setdest <X><Y><speed> 等价于 $mobilenode cmd setdest <X><Y><speed>
         即如果setdest{}不存在->调用基类过程unknown{}->调用cmd{}。argv[0]为cmd,argv[1]为操作名称,argv[2...(argc-1)]其他参数。参数作为字符串传递,须转化为适当的数据类型。

        我们把通过command()执行的操作叫做instproc-likes。command()必须返回TCL_OK或TCL_ERROR表明操作成功或失败。command()可以调用父类的command()。

    《NS与网路模拟》学习1——TCL和OTCL语言

         Tcl是Tool Command Language的缩写。包括两个部分:一种脚本语言和相应的解释器(能方便地向应用程序中添加)。TCL只支持字符串这种数据结构,命令基本语法为:Command arg1 arg2 arg3...。用换行或者分号结束一条命令,用#或;#来注释。

         组合和替代是TCL中的重要语法,Tcl解释器在解析命令参数前,会从左到右遍历参数,首先进行组合,遇到需要替换的部分进行替代,最后才调用这个命令。替代包括变量替代($)、命令替代([])以及反斜杠替代(\);组合主要用来处理空格,包括花括号({})和双引号(""),其中花括号内不允许进行替代。程序中使用了花括号来组合条件表达式和命令体。TCL的各种命令参见书中的详细内容。

         OTCL是ObjectTCL,引入了类和对象的概念。

    1) 类和对象的定义:关键字Class。如Class Animal;Animal animal_1;

       可以通过info命令查看类和对象之间的关系。例如:info+class(查看类)、+instance(查看对象)、+superclass(查看父类)、+heritage(查看继承树)

    2) 成员变量和函数的定义:Otcl中定义成员函数采用关键字instproc,定义成员变量采用关键字instvar。Otcl中成员变量并不预先定义,在成员函数中定义再使用,因此,当其他成员函数使用该变量时需重新申明。如

    Animal instproc run {speed} {
       $self instvar speed_
       set speed_ $speed
       puts "Animal run with speed $speed_"
    }                           其中$self的含义和C++中的this类似

    3) 对象的初始化和销毁:采用init函数来进行初始化,destroy函数来完成析构过程。如

    Animal instproc init {args} {
         $self set speed_ 0
         eval $self next $args
    }

    Animal instproc destroy {} {
         puts "zap!"
         $self next
    }                            其中next函数的意义是父类中的同名函数

    4) 继承:用到superclass关键字。Otcl中成员函数和变量的属性均是public,可以被子类继承。Otcl中所有的类都继承自Object类或它的后代。

       Otcl中子类也可以重写父类的成员函数。Otcl中所有成员函数都是虚函数,并且子类的成员函数同样使用next关键字来调用父类被覆盖的函数。

    IMEP

        IMEP是Internet MANET Encapsulation Protocol的缩写,位于网络层。主要功能包括(翻译自IMEP draft):

        1)通过封装和聚合多个MANET控制分组(如路由分组、ACK、链路状态感知分组、“网络层”地址解析等)或一个大的IMEP消息,减少网络控制分组广播数目,从而提高网络性能。

        2)从多个独立协议中提炼出共同的功能到一个统一的公用的协议中(如链路状态感知、邻近路由器安全认证、一跳邻居广播、控制分组可靠性等)。

        包括以下几个部分:Message Aggregation(AGGR)、Network-layer Address Resolution(NARP)、Link/Connection Status Sense(LCSS)、Broadcast Reliability(REL)、Multipoint Relaying(MPR)以及Authentication(AUTH)。我们主要关注其中的LCSS功能,在ns的TORA仿真协议中,利用IMEP子层来感知链路状态。通过考察IMEP代码和TORA部分代码,总结IMEP在ns中的LCSS功能应用如下:

    1.在**agent.h中,加入IMEPAgent

      #include <imep/imep.h>
      class **agent: {
          ......
          imepAgent  *imepagent;      //a handle to the IMEP layer
          ......
      }

    2.在**agent.c中,初始化函数中加入imepagent=0;

    3.command()函数中加入例如下面的句子(imepagent需要通过imepRegister注册):

       else if(strcmp(argv[1],"imep-agent")==0) {
         imepagent = (imepAgent*)TclObject::lookup(argv[2]);
        if(imepagent == 0)
              return TCL_ERROR;
            imepagent->imepRegister((rtAgent*)this);
        return TCL_OK;
       }

    4.imep中与LCSS相关的部分函数:

      void
         imepAgent::imepRegister(rtAgent *rt) {
              rtagent_ = rt;
              assert(rtagent_);
         }

      其中rtAgent *rtagent_ //pointer to the "upper layer" routing agent,用来指向IMEP上我们自己的路由协议。

      与链路状态设置相关函数:

      下列函数由imep_hello_input或者imep_ack_input调用

      imepAgent::imepSetLinkBiStatus(nsaddr_t index){
           imepSetLinkInStatus(index);
           imepSetLinkOutStatus(index);
      }

      其中包含的两函数中都由以下语句:rtagent_->rtNotifyLinkUP(index)

      另外,通过调用imepagent->imepGetBiLinks(nblist,nbcnt)可以获得完整的邻居列表。

    5.因此,我们所要做的就是定义**agent中的函数rtNotifyLinkUP(index),将其作为路由协议的Hook(如tora_api.c中)。

    August 08

    在NS2中用GDB来调试

         我想对于用NS来仿真的人来说,很重要的一个问题就是调试,因为并没有像VC那样方便的集成的调试环境。《NS与网络模拟》的书中介绍了tcl debug和KDevelop调试的方法,这里主要介绍gdb调试的方法。因为偶个人第一次写ns代码调试用的就是gdb,感觉安装使用都很方便,这里简单介绍一下:

         1.重新运行cygwin的setup文件,选择gdb组件,下载安装。

         2.修改Makefile,添加调试信息,即CCOPT = -g//(后面可能还有其他参数,保留),其实就是在ns编译的时候gcc后面添加-g选项。

         3.重新编译NS2:make clean,make depend然后make

           我make的时候indep-utils\webtrace-conv\dex\proxytrac2any.cc出了声明错误,在main函数前添加extern int IsLittleEndian(void);extern void ToOtherEndian(TEntry *e);

         常用命令:

         进入gdb调试状态:$gdb ns

         运行脚本:<gdb>r file.tcl

         设置断点:<gdb>b file.cc:行数

         删除断点:<gdb>d b 断点编号(1,2,...)

         显示变量或函数值:<gdb>display var

         删除变量或函数值显示:<gdb>d d 变量编号

         单步执行:<gdb>n

         单步跳入:<gdb>s

         循环执行:<gdb>c

         列出运行栈的内容:<gdb>bt——主要针对遇到segment fault的情况

         退出调试:<gdb>q    

    Cygwin 1.5.24 + ns-allinone-2.29

          记得第一次安装NS2,就是在windows下,当时在师姐的指导下,下载安装了一堆东西(貌似当时没有用allinone),修改了环境变量什么的,还修正了一系列莫名其妙的错误才把ns艰难地装上。对于当时的我来说,很多操作都不知道为什么,更不用说记住了。于是,之后一直在Linux跑NS2,实在是害怕了在windows下装。现在,NS2的安装也越来越方便了,有allinone包不说,NS2.27之后的版本,只需要先安装Cygwin就很容易搞定了。基本过程都是参照官方网站上来做的,没有遇到什么太大问题,一些小细节而已~~

          用setup.exe安装Cygwin,注意以下几点:

          1.用UNIX text模式(缺省)而不要用DOS,检验命令:mount|grep textmode                                    

          2.保证Cygwin的安装目录中没有空格(缺省)

          3.login name中不要有空格(偶好像都木有设哎。。。。)

          4.nam需要安装X11,Xfree86或者X.org(xorg-x11-bin,xorg-x11-bin-dlls,xorg-x11-devel,xorg-x11,libs-data和xorg-x11-etc),这个版本的Cygwin是安装X.org

          5.为了安装ns2,还需要安装以下包:diffutils, gcc-g++, gawk, tar, gzip, make, patch, perl和w32api。另外,为了以后的调试工作,还需要安装gdb组件。

          之后ns的安装与linux下的差不多,下载,解压之后,输入命令./install,注意这里可能是由于环境变量设置等原因,可能会显示gdb未安装,不用理会,继续即可。安装完成后照例要修改环境变量,在~/.bashrc中修改。

    怎样使用NS自己的链表(zz from NS2 &SeaSon)

    1.头文件  #include<lib/bsd-list.h>
     
    2.初始化(宏定义)
    1) 在链表节点中添加LIST_ENTRY结构,包括了指向前驱和后继节点的指针。
    #define LIST_ENTRY(type)                                    
    struct {                                                      
        type *le_next;      /* next element */                    
        type **le_prev;     /* address of previous next element */    
    } 
     
        2) 定义一个链表头,链表节点类型为type,表头为lh_first
        #define LIST_HEAD(name, type)                                       
    struct name {                                                       
           type *lh_first;      /* first element */       
    }
     
    3) 初始化链表
    #define     LIST_INIT(head) {                                 
        (head)->lh_first = NULL;                               
    }
     
    4) 插入节点
    #define LIST_INSERT_AFTER(listelm, elm, field) {               
        if (((elm)->field.le_next = (listelm)->field.le_next) != NULL)     
               (listelm)->field.le_next->field.le_prev = &(elm)->field.le_next;                           
        (listelm)->field.le_next = (elm);                           
        (elm)->field.le_prev = &(listelm)->field.le_next;         
    }
     
    #define LIST_INSERT_BEFORE(listelm, elm, field) {                   
        (elm)->field.le_prev = (listelm)->field.le_prev;            
        (elm)->field.le_next = (listelm);                           
        *(listelm)->field.le_prev = (elm);                          
        (listelm)->field.le_prev = &(elm)->field.le_next;         
    }
     
    #define LIST_INSERT_HEAD(head, elm, field) {                           
        if (((elm)->field.le_next = (head)->lh_first) != NULL)         
               (head)->lh_first->field.le_prev = &(elm)->field.le_next;
        (head)->lh_first = (elm);                               
        (elm)->field.le_prev = &(head)->lh_first;                    
    }
     
    5) 删除节点
    #define LIST_REMOVE(elm, field) {                               
        if ((elm)->field.le_next != NULL)                          
               (elm)->field.le_next->field.le_prev = (elm)->field.le_prev;                       
        *(elm)->field.le_prev = (elm)->field.le_next;               
    }
     
    3一个例子
      1) 定义节点类型
    class MFlood_RTEntry {
        friend class MFlood_RTable;
        friend class MFlood;
    public:
        MFlood_RTEntry();
        MFlood_RTEntry(nsaddr_t src,u_int32_t seq);
        bool       isNewSeq(u_int32_t seq);    // old -> false, new->true
        void        addSeq(u_int32_t seq);       // add a seqno to seqno array(rt_seqnos)
    protected:
        LIST_ENTRY(MFlood_RTEntry) rt_link;
        nsaddr_t src_;
    //  u_int32_t seq_;
        u_int32_t              rt_seqnos[REM_SEQ_COUNT]; //seqno array
        u_int32_t       max_seqno;   //max seqno
        u_int32_t       min_seqno;   //max seqno
        u_int16_t              seq_it;    // seqno's iterator
    };
     
      2) 建立一个链表节点类型为MFlood_RTEntry的链表 rthead
      LIST_HEAD(, MFlood_RTEntry) rthead;
     
     3) 初始化链表rheadNULL
    LIST_INIT(&rthead)
     
    4) 怎样使用
    MFlood_RTEntry* 
    MFlood_Rtable::rt_lookup(nsaddr_t id) {
      Mflood_RTEntry *rt = rthead.lh_first;//获取链表表头
      for(; rt; rt = rt->rt_link.le_next) {
             if(rt->src_ == id)
                    break;
      }
      return rt;
    }
     
        5) 删除节点
    void
    MFlood_RTable::rt_delete(nsaddr_t id) {
      MFlood_RTEntry *rt = rt_lookup(id);
      if(rt) {
             LIST_REMOVE(rt, rt_link);
             delete rt;
      }
    }
        6) 插入节点
    rt = new MFlood_RTEntry(ih->saddr(), fh->seq_);
                  LIST_INSERT_HEAD(&rtable_.rthead,rt,rt_link);     
     
    双链表示意图:
    200704133081176442967641  
     

    关于NS2的学习

         学习NS2已经很久,从最初的不知道从哪里开始到现在能够仿真自己的协议,小小的进步。
         问题大大的有,经常在同一个问题上花费冤枉的时间,究其原因是因为偶的记性太差了!
         因此,想要在这里记录下,平时学习时经常遇到的问题的解答,以及用ns2过程中的一些想法与心得。。。