美文网首页NS3(Network Simulator)
NS3 Attribute和Config内容翻译

NS3 Attribute和Config内容翻译

作者: shawn168 | 来源:发表于2017-06-25 20:28 被阅读569次

    以下是ns3手册内容翻译


    在 ns-3 的模拟中,主要有两个方面的设置:

    • 模拟拓扑和对象是如何连接的。
    • 在拓扑中实例化的模型所使用的值。

    本章的重点在于上述的第二项:ns-3 中使用的大量的值是如何组织的、记录的
    以及 ns-3 的用户如何来修改这些值。对于跟踪和统计信息是如何在模拟器中收
    集的,ns-3 的属性系统也起到了支柱作用。

    在深入研究属性值系统之前,回顾 class ns3::Object 的一些基本属性会很有帮
    助。


    3.1 对象概述

    ns-3 本质上是一个基于 C++对象的系统。这意味着新的 C++类(类型)可以像
    往常一样被声明、定义以及子类化。
    许多 ns-3 的对象继承基类 ns3::Object。这些对象有很多附加的属性,这些属
    性是我们为了对系统进行组织和以及改进对象的内存管理而开发的:
    “metadata” 系统将类名与大量的 meta-information 进行链接,这些
    meta-informatino 是与对象有关的,包括子类的基类、子类中可访问的构造函数集以及子类的 “attributes” 集。
    a reference counting smart pointer implementation, for memory management.
    使用属性系统的 ns-3 对象是从 ns3::Object 或 ns3::ObjectBase 派生的。我们
    将要讨论的大多数 ns-3 对象派生自 ns3::Object,但 一些在 smart pointer 内
    存管理框架之外的对象派生自 ns3::ObjectBase。
    让我们回顾一下这些对象的一些属性。


    3.1.1 Smart pointers

    正如 ns-3 tutorial 中介绍的,
    ns-3 的对象的内存管理师通过 reference counting
    smart pointer implementation,class ns3::Ptr 实现的。
    Smart pointers 在 ns-3 的 API 被广泛的使用,来避免 将引用传递给堆分配的
    对象而可能引起内存泄漏。对于大多数基本的用法(语法),将 smart pointer
    看待为常规指针:

    Ptr<WifiNetDevice> nd = ...;
    nd->CallSomeFunction ();
    // etc.
    

    3.1.2 创建对象

    正如我们在@ref{Object Creation}中讨论过的,在最底层的 API 中,具有
    ns3::Object 类型的对象不是像通常一样用 operator new 实例化的,而是通过
    一个叫做 CreateObject() 的模板函数实例化的。
    创建这样的对象的典型方法如下:

    Ptr<WifiNetDevice> nd = CreateObject<WifiNetDevice> ();
    

    你可以认为这在功能上等价于:

    WifiNetDevice* nd = new WifiNetDevice ();
    

    由 ns3::Object 派生的对象一定是通过 CreateObject()被分配在堆上。而由
    ns3::ObjectBase 派生的对象,
    比如 ns-3 的 helper functions and packet headers
    and trailers, 可以被分配在栈上。在一些脚本中,你可能看不到大量的 CreateObject()调用,那时因为存在很多
    helper objects,他们为你执行了 CreateObject()s。


    3.1.3 TypeId

    ns-3 中由类 ns3::Object 派生出的类可以包含一个叫做 TypeId 的元数据类,
    该类记录关于类的元信息,以便在对象聚合以及构件管理系统中使用:
    识别该类的一个独一无二的字符串。a unique string identifying the class
    在元数据系统中,子类的基类。
    子类中 可访问的构造函数的集合。


    3.1.4 对象小结

    将所有这些概念综合在一起,让我们研究一个特定的例子:class ns3::Node。
    公共头文件 node.h 中的一个声明包含了一个静态的 GetTypeId 函数调用:

    class Node : public Object
    {
    public:
    static TypeId GetTypeId (void);
    ...
    该函数在文件 node.cc 中定义如下:
    TypeId
    Node::GetTypeId (void)
    {
    static TypeId tid = TypeId (“ns3::Node”)
    .SetParent<Object> ()
    ;
    return tid;
    }
    

    最终,当用户要创建节点时,可以这样调用:

    Ptr<Node> n = CreateObject<Node> ();
    

    下边我们会讨论属性(与类的成员变量以及成员函数相关联的值)是如何被加入上述 TypeId 的。


    3.2 属性概述

    属性系统的目标是组织对模拟的内部成员对象的访问。这个目标的产生式因为:
    通常在模拟中,用户将剪切、粘贴或修改现存的模拟脚本,或者将使用更高层的
    模拟构造,但通常对研究或跟踪某些特别的内部变量很有兴趣。例如:
    “我只想跟踪第一个接入点的无线接口上的包。”
    “我想跟踪某个特定 TCP 套接字上 TCP 拥塞窗口的值(每次当它发生变化时)”
    “我想获取并记录模拟中所有被使用到的值。”
    类似地,用户可能想对模拟中的内部变量进行细致的访问,或者可能想广泛地改
    变某个特定参数的初始值,以便涉及到所有随后创建的对象。用户还可能希望知
    道在模拟配置中哪些变量是可以设置的和可以获得的。这不仅仅是为了命令行下
    直接交互,还考虑到(将来的)图形用户界面,该界面可能提供让用户在节点上
    右击鼠标就能获得信息的功能,这些信息可能是一个层次性组织的参数列表,显
    示该节点上可以设置的参数以及构成节点的成员对象,还有帮助信息和每个参数
    的默认值。


    3.2.1 功能概述 Functional overview

    我们给用户提供可以访问系统深处的值的方法,而不用在系统中加入存取器(指
    针)并通过指针链来获取想要的值。考虑类 DropTailQueue,该类有一个叫做
    m_maxPackets 的无符号整型成员变量,该成员变量控制队列的大小。
    查看 DropTailQueue 的声明,我们看到:

    class DropTailQueue : public Queue {
    public:
    static TypeId GetTypeId (void);
    ...
    private:
    std::queue<Ptr<Packet> > m_packets;
    uint32_t m_maxPackets;
    };
    

    考虑用户可能对 m_maxPackets 的值想要做的事情:为系统设置一个默认值,以便无论何时一个新的 DropTailQueue 被创建时,这
    个成员变量都被初始化成该默认值。
    对一个已经被实例化过的队列,设置或获取队列的该值。
    上述情况通常需要提供 Set()和 Get()函数,以及某些类型的全局默认值。
    在 ns-3 的属性系统中,这些值定义和存取器函数被移入类 TypeId,例如:

    TypeId DropTailQueue::GetTypeId (void)
    {
    static TypeId tid = TypeId (“ns3::DropTailQueue”)
    .SetParent<Queue> ()
    .AddConstructor<DropTailQueue> ()
    .AddAttribute (“MaxPackets”,
    “The maximum number of packets accepted by this DropTailQueue.”,
    UintegerValue (100),
    MakeUintegerAccessor (&DropTailQueue::m_maxPackets),
    MakeUintegerChecker<uint32_t> ())
    ;
    return tid;
    }
    

    方法 AddAttribute()对该值进行一系列处理:
    将变量 m_maxPackets 绑定到一个字符串”MaxPackets”。
    提供默认值(100 packets)
    提供为该值下定义的帮助信息。
    提供”checker”(本例中未使用),可以用来设置所允许的上下界。
    关键的一点是,现在该变量的值以及它的默认值在属性名字空间中是可访问的,
    基于字符串”MaxPackets”和 TypeId 字符串。在下一节,我们将提供一个例子
    来说明用户如何来操纵这些值。


    3.2.2 基本用法

    我们研究一下用户脚本如何访问这些值,基于文件
    samples/main-attribute-value.cc,略去了一些细节。

    //
    // This is a basic example of how to use the attribute system to// set and get a value in the underlying system; namely, an unsigned
    // integer of the maximum number of packets in a queue
    //
    int
    main (int argc, char *argv[])
    {
    // By default, the MaxPackets attribute has a value of 100 packets
    // (this default can be observed in the function DropTailQueue::GetTypeId)
    //
    // Here, we set it to 80 packets. We could use one of two value types:
    // a string-based value or a Uinteger value
    Config::SetDefault (“ns3::DropTailQueue::MaxPackets”, StringValue (“80′′));
    
    // The below function call is redundant
    Config::SetDefault (“ns3::DropTailQueue::MaxPackets”, UintegerValue (80));
    
    // Allow the user to override any of the defaults and the above
    // SetDefaults() at run-time, via command-line arguments
    CommandLine cmd;
    cmd.Parse (argc, argv);
    

    需要注意的是对 Config::SetDefault 的两次调用。这表明了我们如何设置随后
    被实例化的 DropTailQueues 的默认值。我们举例说明了两种类型的 Value 类:
    类 StringValue 和类 UintegerValue,这两个类可以被用来将值赋给叫做
    “ns3::DropTailQueue::MaxPackets” 的属性。
    现在我们使用底层 API 来创建一些对象。由于上述对默认值的操作,新建的队
    列的 m_maxPackets 值将被初始化为 80 而不是 100。

    Ptr<Node> n0 = CreateObject<Node> ();
    Ptr<PointToPointNetDevice> net0 = CreateObject<PointToPointNetDevice>
    ();
    n0->AddDevice (net0);
    Ptr<Queue> q = CreateObject<DropTailQueue> ();
    net0->AddQueue(q);
    

    这里我们创建了一个单独的节点(节点 0)和一个单独的PointToPointNetDevice(NetDevice 0),并将一个 DropTailQueue 加入到了
    该 PointToPointNetDevice 上。
    现在,我们可以操纵已经实例化过的 DropTailQueue 的 MaxPackets 值了,有
    多种不同的方法来达到这个目的。


    3.2.2.1 基于指针的存取 Pointer-based access

    假定存在一个指向相关网络设备的智能指针(Ptr),例如这里 net0 的指针。
    改变该值的一种方法是 通过存取指向底层的队列的指针,并修改该队列的属性。
    首先,我们能够通过 PointToPointNetDevice 的属性获得指向队列(基类)的指
    针,该属性叫做 TxQueue。

    PointerValue tmp;
    net0->GetAttribute (“TxQueue”, tmp);
    Ptr<Object> txQueue = tmp.GetObject ();
    

    使用函数 GetObject ,我们能够执行到 DropTailQueue 的安全的 downcast,
    MaxPackets 是 DropTailQueue 的成员。

    Ptr<DropTailQueue> dtq = txQueue->GetObject <DropTailQueue> ();
    NS_ASSERT (dtq != 0);
    

    现在我们能够获取该队列上属性的值。由于属性系统存储的是值,而不是类型,
    所以与 Java 类似地,我们为底层数据类型引入了封装的 “Value” 类。这里,属
    性值被赋值给了一个 UintegerValue ,对这个值应用 Get()方法会得到

    (unwrapped)uint32_t。
    UintegerValue limit;
    dtq->GetAttribute (“MaxPackets”, limit);
    NS_LOG_INFO (“1. dtq limit: ” << limit.Get () << ” packets”);
    

    注意上述的 downcast 不是必须的。尽管该属性是该子类的成员,我们依然能够
    使用 Ptr<Queue> 来做同样的事情。

    txQueue->GetAttribute (“MaxPackets”, limit);
    NS_LOG_INFO (“2. txQueue limit: ” << limit.Get () << ” packets”);
    现在,让我们将他设置为另一个值(60)。txQueue->SetAttribute(“MaxPackets”, UintegerValue (60));
    txQueue->GetAttribute (“MaxPackets”, limit);
    NS_LOG_INFO (“3. txQueue limit changed: ” << limit.Get () << ” packets”);
    

    3.2.2.2 基于名字空间的存取 Namespace-based access

    另一种获取属性的方法是使用 configuration namespace。属性位于这个名字空
    间的已经路径上。如果用户无法访问底层指针但又想要使用一条语句来配置某个
    特定的属性时,这个方法很有用。

    Config::Set (“/NodeList/0/DeviceList/0/TxQueue/MaxPackets”,
    UintegerValue (25));
    txQueue->GetAttribute (“MaxPackets”, limit);
    NS_LOG_INFO (“4. txQueue limit changed through namespace: ” <<
    limit.Get () << ” packets”);
    我们还可以使用通配符来设置所有节点和所有网络设备的该值(例子如下)。
    Config::Set (“/NodeList/*/DeviceList/*/TxQueue/MaxPackets”,
    UintegerValue (15));
    txQueue->GetAttribute (“MaxPackets”, limit);
    NS_LOG_INFO (“5. txQueue limit changed through wildcarded namespace: ”
    <<
    limit.Get () << ” packets”);
    

    3.2.3 通过构造函数和 helper classes 来设置 Setting through constructors

    helper classes
    任意的属性组合都可以由 helper 和底层 APIs 来设置和获得。通过构造函数本身:

    Ptr<Object> p = CreateObject<MyNewObject> (“n1′′, v1, “n2′′, v2, ...);
    

    通过高层 helper APIs,比如:

    mobility.SetPositionAllocator (“GridPositionAllocator”,
    “MinX”, DoubleValue (-100.0),
    “MinY”, DoubleValue (-100.0),
    “DeltaX”, DoubleValue (5.0),“DeltaY”, DoubleValue (20.0),
    “GridWidth”, UintegerValue (20),
    “LayoutType”, StringValue (“RowFirst”));
    

    3.2.4 值类 Value classes

    读者将注意到新的某 Value 类是 AttributeValue 基类的子类。这些类可以被看做
    中间类,这些中间类可以被用来将 raw types 转换为可以被属性系统使用的值。
    属性系统的数据库用一种一般类型来存储许多类型的对象,到该一般类型的转换
    可以使用中间类(IntegerValue, DoubleValue for “floating point”)来完成,也
    可以通过字符串来完成。从类型到值的直接隐式转换不是很可行,所以用户可以
    选择使用字符串还是值:

    p->Set (“cwnd”, StringValue (“100′′)); // string-based setter
    p->Set (“cwnd”, IntegerValue (100)); // integer-based setter
    

    对于用户想引入属性系统的新的类型,系统提供一些宏来帮助用户为新的类型声
    明和定义新的 AttributeValue 子类。

    ATTRIBUTE_HELPER_HEADER
    ATTRIBUTE_HELPER_CPP
    

    3.3 对属性进行扩展 Extending attributes

    ns-3 系统在属性系统下边放置了许多内部值,但毫无疑问,用户将对系统不完
    善的地方进行扩展以及加入用户自己的类。


    3.3.1 将现存的内部变量加入元数据系统 Adding an existing internal variable to the metadata system

    考虑类 TcpSocket 中的这个变量:

    uint32_t m_cWnd; // Congestion window
    

    假设 使用 Tcp 的某个人想要使用元数据系统获得或设置该变量的值。如果 ns-3
    还没有提供这个,用户可以再元数据系统中添加如下声明

    (在 TcpSocket 的
    TypeId 声明中):.AddParameter (“Congestion window”,
    “Tcp congestion window (bytes)”,
    Uinteger (1),
    MakeUintegerAccessor (&TcpSocket::m_cWnd),
    MakeUintegerChecker<uint16_t> ());
    

    现在,用户可以使用指向该 TcpSocket 的指针来执行设置和获取操作,而不用显
    式添加这些函数。此外,访问控制可以被应用,比如使得该参数只读不可写和对
    参数进行上下界检查。


    3.3.2 添加新的 TypeId Adding a new TypeId

    现在我们讨论用户如何往 ns-3 系统中添加新的类。
    我们已经介绍过类似如下的 TypeId 定义:

    TypeId
    RandomWalk2dMobilityModel::GetTypeId (void)
    {
    static TypeId tid = TypeId (“ns3::RandomWalk2dMobilityModel”)
    .SetParent<MobilityModel> ()
    .SetGroupName (“Mobility”)
    .AddConstructor<RandomWalk2dMobilityModel> ()
    .AddAttribute (“Bounds”,
    “Bounds of the area to cruise.”,
    RectangleValue (Rectangle (0.0, 0.0, 100.0, 100.0)),
    MakeRectangleAccessor (&RandomWalk2dMobilityModel::m_bounds),
    MakeRectangleChecker ())
    .AddAttribute (“Time”,
    “Change current direction and speed after moving for this delay.”,
    TimeValue (Seconds (1.0)),
    MakeTimeAccessor (&RandomWalk2dMobilityModel::m_modeTime),
    MakeTimeChecker ())
    // etc (more parameters).
    ;
    return tid;
    }
    

    类声明中与此相关的声明是一行公共成员方法:

    public:
    static TypeId GetTypeId (void);
    

    典型的错误包括:
    没有调用 SetParent 方法,或者使用了错误的类型来调用他。
    没有调用 AddConstructor 方法,或者使用了错误的类型来调用他。
    在 TypeId 的构造函数中对于 TypeId 的名字引入了印刷错误。
    没有使用封装类的全限定 C++类型名作为 TypeId 的名字。
    以上错误都无法被 ns-3 探测到,所以用户应当多次检查以确保正确性。


    3.4 给属性系统中添加新的类

    从用户的角度来看,编写新的类并将其加入属性系统主要是编写字符串与属性值
    之间的转换。多数可以通过“宏化的“(macro-ized)代码来复制/粘贴。例如目
    录 src/mobility/ 下的类 Rectangle:
    类声明中加入一行:

    /**
    * brief a 2d rectangle
    */
    class Rectangle
    {
    ...
    };
    

    在类声明的下边加入一个宏调用和两个操作符:

    std::ostream &operator << (std::ostream &os, const Rectangle &rectangle);
    std::istream &operator >> (std::istream &is, Rectangle &rectangle);
    ATTRIBUTE_HELPER_HEADER (Rectangle);
    

    类定义的代码类似于:

    ATTRIBUTE_HELPER_CPP (Rectangle);
    std::ostream &
    operator << (std::ostream &os, const Rectangle &rectangle){
        os << rectangle.xMin << “|” << rectangle.xMax << “|” << rectangle.yMin <<
        “|” << rectangle.yMax;
        return os;
    }
    std::istream &
    operator >> (std::istream &is, Rectangle &rectangle)
    {
        char c1, c2, c3;
        is >> rectangle.xMin >> c1 >> rectangle.xMax >> c2 >> rectangle.yMin >>
        c3 >> rectangle.yMax;
        if (c1 != ‘|’ ||c2 != ‘|’ ||c3 != ‘|’)
        {
            is.setstate (std::ios_base::failbit);
        }
        return is;
    }
    

    这些流操作符将字符串表示形式的 Rectangle (“xMin|xMax|yMin|yMax”)转
    化为底层的 Rectangle,模块的编写者必须指定新类的这些操作符以及该类的实
    例的字符串句法表示形式。


    3.5 ConfigStore

    请求反馈: 这是 ns-3 的一个试验性的特色。他不在主要的代码树中。如果您
    喜欢该特色并愿意提供关于他的反馈,请给我们写电子邮件。
    ns-3 的属性的值可以被存储在 ascii 文本文件中,并在将来的模拟中加载。这个
    特色被认为是 ns-3 的 ConfigStore。 ConfigStore 的代码在 src/contrib/ 下。因
    为我们还在寻求用户的反馈,所以目前还不在主要的代码树中。
    我们用一个例子来探索这个系统。将文件 csma-bridge.cc 复制到 scratch 目录:
    cp examples/csma-bridge.cc scratch/
    ./waf我们编辑该文件以加入 ConfigStore 特色。首先,添加一个 include 语句,然后
    加入以下行:

    #include “contrib-module.h”
    ...
    int main (...)
    {
    // setup topology
    // Invoke just before entering Simulator::Run ()
    ConfigStore config;
    config.Configure ();
    Simulator::Run ();
    }
    

    存在一个控制 Configure()的属性,他决定 Configure()是将模拟的配置存储在文
    件中并退出,还是加载模拟的配置文件并继续执行。首先,属性 LoadFilename
    被检查,如果不为空,则程序从所提供的文件名来加载配置;如果为空,且属性
    StoreFilename 被提供,则配置将被写入指定的输出文件。
    虽然生成一个配置文件的样本并修改一些值是可能的,但有些情况这种方法是行
    不通的,因为对于同一个自动生成的配置文件,同一个对象上的同一个值可能在
    不同的配置路径上出现多次。
    同样地,使用这个类的最好方法是用他生成一个初始的配置文件,仅从该文件中
    提取严格必须得元素,并将这些元素移动一个新的配置文件。这个新的配置文件
    在随后的模拟中可以被安全地编辑和加载。
    以此为例运行一次程序来创建一个配置文件。如果你使用的是 bash shell,那么
    下边的命令应该能够工作(阐明了如何从命令行设置属性):

    ./build/debug/scratch/csma-bridge –
    ns3::ConfigStore::StoreFilename=test.config
    

    如果上述命令不起作用(上述命令需要 rpath 的支持),试试如下:

    ./waf –command-template=”%s –
    ns3::ConfigStore::StoreFilename=test.config” –run scratch/csma-bridge
    运行该程序将产生一个叫做”test.config”的输出配置文件,类似于如下:
    /$ns3::NodeListPriv/NodeList/0/$ns3::Node/DeviceList/0/$ns3::CsmaNet
    Device/Addre
    ss 00:00:00:00:00:01
    /$ns3::NodeListPriv/NodeList/0/$ns3::Node/DeviceList/0/$ns3::CsmaNet
    Device/Frame
    Size 1518
    /$ns3::NodeListPriv/NodeList/0/$ns3::Node/DeviceList/0/$ns3::CsmaNet
    Device/SendE
    nable true
    /$ns3::NodeListPriv/NodeList/0/$ns3::Node/DeviceList/0/$ns3::CsmaNet
    Device/Recei
    veEnable true
    /$ns3::NodeListPriv/NodeList/0/$ns3::Node/DeviceList/0/$ns3::CsmaNet
    Device/TxQue
    ue/$ns3::DropTailQueue/MaxPackets 100
    /$ns3::NodeListPriv/NodeList/0/$ns3::Node/DeviceList/0/$ns3::CsmaNet
    Device/Mtu 1
    500
    ...
    

    上边列出了拓扑脚本中的每一个对象以及每一个注册过的属性的值。此文件的语
    法是每行都标明了属性独一无二的名字,名字后边是值。
    该文件是某个给定模拟中的参数的一个方便的记录,可以使用模拟输出文件来存
    储。此外,该文件还可以被用来将模拟参数化,而不是编辑脚本或传递命令行参
    数。比如:检查并调整一个已经存在的配置文件中的值,然后将该文件传递给模
    拟程序。相关的命令:

    ./build/debug/scratch/csma-bridge –
    ns3::ConfigStore::LoadFilename=test.config
    如果上述命令不起作用(上述命令需要 rpath 的支持),试试如下:
    ./waf –command-template=”%s –
    ns3::ConfigStore::LoadFilename=test.config” –run scratch/csma-bridge
    

    3.5.1 基于 GTK 的 ConfigStore GTK-based ConfigStore

    对于 ConfigStore,存在一个基于 GTK 的前端。这使得用户可以使用 GUI 来存
    取和修改变量。该特色的屏幕截图可以在 ns-3 Overview 找到。
    要使用这个特色,必须安装 libgtk 和 libgtk-dev。Ubuntu 下安装命令的示例:
    sudo apt-get install libgtk2.0-0 libgtk2.0-dev
    通过 ./waf configure 阶段的输出来检验是否已经配置好:

    —- Summary of optional NS-3 features:
    Threading Primitives                : enabled
    Real Time Simulator                 : enabled
    GtkConfigStore                      : not enabled (library ‘gtk+-2.0 >= 2.12′ not found)
    

    在上述例子中,GtkConfigStore 没有开启,要想使用他,必须安装合适的版本,
    并且再次执行 ./waf configure; ./waf。
    用法与 non-GTK-based 版本几乎一样:

    // Invoke just before entering Simulator::Run ()
    GtkConfigStore config;
    config.Configure ();
    

    现在,当你运行脚本时将弹出一个 GUI,使得你可以打开不同节点/对象上属性
    的菜单,配置好之后启动模拟。


    3.5.2 将来的工作 Future work

    可能存在的改进:

    • 在文件起始处保存一个包含日期与时间的独一无二的版本号。
    • 在某处保存 rng 的初始种子。
    • 使每个 RandomVariable 都连续化(serialize)自己的初始种子并在后期重新读取。
    • 加入默认值。

    相关文章

      网友评论

        本文标题:NS3 Attribute和Config内容翻译

        本文链接:https://www.haomeiwen.com/subject/sxsgnttx.html