首页 > 代码库 > 9、ns-3内核

9、ns-3内核

在官方文档ns-3-manual.pdf中,整个框架图:

clip_image002

v 随机变量

伪随机生成器pseudo-random number generator (PRNG)。

默认情况下,ns-3仿真程序使用固定种子和运行数字,它们分别存在全局变量g_rngSeed 和 g_rngRun中,在ns-3.14版本以后,ns3::RandomVariableStream

代替ns3::RandomVariable,但是底层不变。

可用ns3::RngSeedManager类来控制种子的属性。在开始时ns3::RngSeedManager::SetSeed()设置种子。在程序开始时,可以用ns3::RngSeedManager::SetRun()运行随机数。

RngSeedManager::SetSeed (3); // Changes seed from default of 1 to 3

RngSeedManager::SetRun (7);// Changes run number from default of 1 to 7

例子:/ns-3.19/src/core/examples/sample-random-variable-stream.cc

/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */

/*

* Copyright (c) 2012 University of Washington

*

* This program is free software; you can redistribute it and/or modify

* it under the terms of the GNU General Public License version 2 as

* published by the Free Software Foundation;

*

* This program is distributed in the hope that it will be useful,

* but WITHOUT ANY WARRANTY; without even the implied warranty of

* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

* GNU General Public License for more details.

*

* You should have received a copy of the GNU General Public License

* along with this program; if not, write to the Free Software

* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

*/

#include "ns3/simulator.h"

#include "ns3/nstime.h"

#include "ns3/command-line.h"

#include "ns3/random-variable-stream.h"

#include <iostream>

using namespace ns3;

/*

* This program can be run from waf such as "./waf --run sample-random-variable-stream"

*

* This program is about as simple as possible to display the use of ns-3

* to generate random numbers. By default, the uniform random variate that

* will be outputted is 0.816532. Because ns-3 uses a fixed seed for the

* pseudo-random number generator, this program should always output the

* same number. Likewise, ns-3 simulations using random variables will

* behave deterministically unless the user changes the RunNumber or the

* Seed.

*

* There are three primary mechanisms to change the seed or run numbers

* from their default integer value of 1

* 1) Through explicit call of SeedManager::SetSeed () and

* SeedManager::SetRun () (commented out below)

* 2) Through the passing of command line arguments such as:

* "./waf --command-template="%s --RngRun=<value>" --run program-name"

* 3) Through the use of the NS_GLOBAL_VALUE environment variable, such as:

* "NS_GLOBAL_VALUE="http://www.mamicode.com/RngRun=" ./waf --run program-name"

*

* For instance, setting the run number to 3 will change the program output to

* 0.775417

*

* Consult the ns-3 manual for more information about the use of the

* random number generator

*/

int main (int argc, char *argv[])

{

CommandLine cmd;

cmd.Parse (argc, argv);

// SeedManager::SetRun (3);

Ptr<UniformRandomVariable> uv = CreateObject<UniformRandomVariable> ();

std::cout << uv->GetValue () << std::endl;

}

如果一直运行,那么它的结果不变。

clip_image004

可以改变参数来运行:

$ NS_GLOBAL_VALUE="http://www.mamicode.com/RngRun=3" ./waf --run program-name

clip_image006

$ ./waf --command-template="%s --RngRun=3" --run program-name

clip_image008

$ ./build/optimized/scratch/program-name --RngRun=3

但是貌似这个运行不了。

随机变量:基类是RandomVariableStream。派生类

• class UniformRandomVariable

• class ConstantRandomVariable

• class SequentialRandomVariable

• class ExponentialRandomVariable

• class ParetoRandomVariable

• class WeibullRandomVariable

• class NormalRandomVariable

• class LogNormalRandomVariable

• class GammaRandomVariable

• class ErlangRandomVariable

• class TriangularRandomVariable

• class ZipfRandomVariable

• class ZetaRandomVariable

• class DeterministicRandomVariable

• class EmpiricalRandomVariable

v 回调

解除依赖性,利用指针,让一个模块调用另外一个模块的函数。

区分是全局,还是类的函数。

如果是全局函数

static double

CbOne (double a, double b)

{

std::cout << "invoke cbOne a=" << a << ", b=" << b << std::endl;

return a;

}

int main (int argc, char *argv[])

{

// return type: double

// first arg type: double

// second arg type: double

Callback<double, double, double> one;

one = MakeCallback (&CbOne);

NS_ASSERT (!one.IsNull ());

// invoke cbOne function through callback instance

double retOne;

retOne = one (10.0, 20.0);

}

如果是类的函数:

class MyCb {

public:

int CbTwo (double a) {

std::cout << "invoke cbTwo a=" << a << std::endl;

return -5;

}

};

int main ()

{

...

// return type: int

// first arg type: double

Callback<int, double> two;

MyCb cb;

// build callback instance which points to MyCb::cbTwo

two = MakeCallback (&MyCb::CbTwo, &cb);

...

}

调用空的回调

two = MakeNullCallback<int, double> ();

NS_ASSERT (two.IsNull ());

v 对象模型

l 面向对象编程

l 对象基类:Object、ObjectBase、SimpleRefCount。属性系统,对象聚合系统,智能指针和引用计数系统。

l 内存管理与引用计数指针(Ptr)

引用计数指针Ptr,智能指针为每个基本类型提供一对函数Ref()和Unref()来增加和减少对象实例的引用计数。记得要用CreateObject模板函数创建类对象。

l 聚合,ns-3使用查询接口设计模式来使用聚合。

例子

static void

AddIpv4Stack(Ptr<Node> node)

{

//创建一个IPv4协议的指针对象。

Ptr<Ipv4L3Protocol> ipv4 = CreateObject<Ipv4L3Protocol> ();

//把IPv4协议聚合到节点中。这样Node就不需要被编辑来使用户使用指向基类Node的指针来访问IPv4接口。用户可以在程序运行时向节点请求指向该节点IPv4接口的指针。

ipv4->SetNode (node);

node->AggregateObject (ipv4);

Ptr<Ipv4Impl> ipv4Impl = CreateObject<Ipv4Impl> ();

ipv4Impl->SetIpv4 (ipv4);

node->AggregateObject (ipv4Impl);

}

如果有一个节点指针m_node,该指针指向一个节点对象,且先前已经將IPv4的实现聚合到该节点。用户想要配置一个路由协议,为了实现这点,必须访问已经聚合到该节点的一个对象,且该用户具有IP配置接口。

Ptr<Ipv4> ipv4 = m_node->GetObject<Ipv4> ();

如果没有IPv4的对象被聚合到该节点上,该方法返回一个NULL。

v 属性系统

仿真时,要设置:对使用的网络构件进行连接组织和模拟拓扑;对网络构件中实例化的模型所使用的参数值进行设置。

基本上类都是继承与ns3::Object 或者 ns3::ObjectBase.ns3类如果从ns3::Object 派生,那么它就有一个元数据metadata类,叫做TypeId,该类记录了关于类的元数据,用来对象聚合和组件管理系统。它包括:一个唯一的字串来标记该类,包括元数据系统的子类,在子类中一系列的可访问的构造函数。例子:

class Node : public Object

{

public:

static TypeId GetTypeId (void);

...

Node.cc文件的定义如下:

TypeId

Node::GetTypeId (void)

{

static TypeId tid = TypeId ("ns3::Node")

.SetParent<Object> ()

.AddConstructor<Node> ()

.AddAttribute ("DeviceList", "The list of devices associated to this Node.",

ObjectVectorValue (),

MakeObjectVectorAccessor (&Node::m_devices),

MakeObjectVectorChecker<NetDevice> ())

.AddAttribute ("ApplicationList", "The list of applications associated to this Node.",

ObjectVectorValue (),

MakeObjectVectorAccessor (&Node::m_applications),

MakeObjectVectorChecker<Application> ())

.AddAttribute ("Id", "The id (unique integer) of this Node.",

TypeId::ATTR_GET, // allow only getting it.

UintegerValue (0),

MakeUintegerAccessor (&Node::m_id),

MakeUintegerChecker<uint32_t> ())

;

return tid;

}

SetParent<Object>()在声明中调用,结合对象聚合机制使用,方便在使用GetObject()函数时安全的进行向上或向下类型转换。

.AddConstructor<Node> ()结合抽象对象工厂机制来使用,允许我们不必要知道类的内部细节而构建对象。

.AddAttribute()调用目的是把一个给定的唯一字符床和类的成员变量相关联。参数从左到右分别是:要绑定的字符串,解释说明,属性的映射变量,把类成员变量转化为第3个参数的类型(其实就是检查)。

有两种初始化方式:

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

和工厂机制初始化

ObjectFactory factory;

const std::string typeId = "ns3::Node’’;

factory.SetTypeId (typeId);

Ptr<Object> node = factory.Create <Object> ();

关于获取和修改属性值:

考虑到有类的声明:

class DropTailQueue : public Queue {

public:

static TypeId GetTypeId (void);

...

private:

std::queue<Ptr<Packet> > m_packets;

uint32_t m_maxPackets;

};

cc文件的描述:

NS_OBJECT_ENSURE_REGISTERED (DropTailQueue);

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;

}

在属性设置中:建立m_maxPackets 到字符串“MaxPackets”的映射,并且默认值为100,提供了关于定义值的帮助文本,提供了checker,可以用来设置所允许的范围。

要注意宏定义NS_OBJECT_ENSURE_REGISTERED (DropTailQueue)是一定要调用的,否则属性系统涉及的属性都不能被初始化。

使用例子在src/point-to-point/examples/main-attribute-value.ccz中

/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */

/*

* Copyright (c) 2008 University of Washington

*

* This program is free software; you can redistribute it and/or modify

* it under the terms of the GNU General Public License version 2 as

* published by the Free Software Foundation;

*

* This program is distributed in the hope that it will be useful,

* but WITHOUT ANY WARRANTY; without even the implied warranty of

* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

* GNU General Public License for more details.

*

* You should have received a copy of the GNU General Public License

* along with this program; if not, write to the Free Software

* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

*

* Author: Tom Henderson <tomh@tomh.org>

*/

#include "ns3/log.h"

#include "ns3/command-line.h"

#include "ns3/ptr.h"

#include "ns3/config.h"

#include "ns3/uinteger.h"

#include "ns3/string.h"

#include "ns3/pointer.h"

#include "ns3/simulator.h"

#include "ns3/node.h"

#include "ns3/queue.h"

#include "ns3/drop-tail-queue.h"

#include "ns3/point-to-point-net-device.h"

using namespace ns3;

NS_LOG_COMPONENT_DEFINE ("AttributeValueSample");

//

// 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[])

{

LogComponentEnable ("AttributeValueSample", LOG_LEVEL_INFO);

// 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 UintegerValue 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);

// Now, we will create a few objects using the low-level API

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

Ptr<PointToPointNetDevice> net0 = CreateObject<PointToPointNetDevice> ();

n0->AddDevice (net0);

Ptr<Queue> q = CreateObject<DropTailQueue> ();

net0->SetQueue (q);

// At this point, we have created a single node (Node 0) and a

// single PointToPointNetDevice (NetDevice 0) and added a

// DropTailQueue to it.

// Now, we can manipulate the MaxPackets value of the already

// instantiated DropTailQueue. Here are various ways to do that.

// We assume that a smart pointer (Ptr) to a relevant network device

// is in hand; here, it is the net0 pointer.

// 1. Pointer-based access

//第一种方法,基于指针的修改属性,可以向下转型也可以不用向下转型。

// One way to change the value is to access a pointer to the

// underlying queue and modify its attribute.

//

// First, we observe that we can get a pointer to the (base class)

// queue via the PointToPointNetDevice attributes, where it is called

// TxQueue

PointerValue ptr;

net0->GetAttribute ("TxQueue", ptr);

Ptr<Queue> txQueue = ptr.Get<Queue> ();

// Using the GetObject function, we can perform a safe downcast

// to a DropTailQueue, where MaxPackets is a member

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

NS_ASSERT (dtq);

// Next, we can get the value of an attribute on this queue

// We have introduced wrapper "Value" classes for the underlying

// data types, similar to Java wrappers around these types, since

// the attribute system stores values and not disparate types.

// Here, the attribute value is assigned to a Uinteger, and

// the Get() method on this value produces the (unwrapped) uint32_t.

UintegerValue limit;

dtq->GetAttribute ("MaxPackets", limit);

NS_LOG_INFO ("1. dtq limit: " << limit.Get () << " packets");

// Note that the above downcast is not really needed; we could have

// done the same using the Ptr<Queue> even though the attribute

// is a member of the subclass

txQueue->GetAttribute ("MaxPackets", limit);

NS_LOG_INFO ("2. txQueue limit: " << limit.Get () << " packets");

// Now, let‘s set it to another value (60 packets)

txQueue->SetAttribute ("MaxPackets", UintegerValue (60));

txQueue->GetAttribute ("MaxPackets", limit);

NS_LOG_INFO ("3. txQueue limit changed: " << limit.Get () << " packets");

// 2. Namespace-based access

//通过命名空间来修改

// An alternative way to get at the attribute is to use the configuration

// namespace. Here, this attribute resides on a known path in this

// namespace; this approach is useful if one doesn‘t have access to

// the underlying pointers and would like to configure a specific

// attribute with a single statement.

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");

// we could have also used wildcards to set this value for all nodes

// and all net devices (which in this simple example has the same

// effect as the previous Set())

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");

Simulator::Destroy ();

}

运行结果:

clip_image010

还可以使用对象名基于服务访问:

Names::Add ("server", serverNode);

Names::Add ("server/eth0", serverDevice);

...

Config::Set ("/Names/server/eth0/TxQueue/MaxPackets", UintegerValue (25));

可以通过构造函数帮助类设置属性:

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

或者高层帮助APIs来设置:

mobility.SetPositionAllocator ("ns3::GridPositionAllocator",

"MinX", DoubleValue (-100.0),

"MinY", DoubleValue (-100.0),

"DeltaX", DoubleValue (5.0),

"DeltaY", DoubleValue (20.0),

"GridWidth", UintegerValue (20),

"LayoutType", StringValue ("RowFirst"));

ConfigStore是属性值和默认值的特殊数据库,它分布在不同的模块src/config-store/ directory中。可以看一个添加例子src/config-store/examples/config-store-save.cc。

/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */

#include "ns3/core-module.h"

#include "ns3/config-store-module.h"

#include <iostream>

using namespace ns3;

class ConfigExample : public Object

{

public:

static TypeId GetTypeId (void) {

static TypeId tid = TypeId ("ns3::ConfigExample")

.SetParent<Object> ()

.AddAttribute ("TestInt16", "help text",

IntegerValue (-2),

MakeIntegerAccessor (&ConfigExample::m_int16),

MakeIntegerChecker<int16_t> ())

;

return tid;

}

int16_t m_int16;

};

NS_OBJECT_ENSURE_REGISTERED (ConfigExample)

;

// Assign a new default value to A::TestInt16 (-5)

// Configure a TestInt16 value for a special instance of A (to -3)

// View the output from the config store

//

int main (int argc, char *argv[])

{

Config::SetDefault ("ns3::ConfigExample::TestInt16", IntegerValue (-5));

Ptr<ConfigExample> a_obj = CreateObject<ConfigExample> ();

NS_ABORT_MSG_UNLESS (a_obj->m_int16 == -5, "Cannot set ConfigExample‘s integer attribute via Config::SetDefault");

Ptr<ConfigExample> b_obj = CreateObject<ConfigExample> ();

b_obj->SetAttribute ("TestInt16", IntegerValue (-3));

IntegerValue iv;

b_obj->GetAttribute ("TestInt16", iv);

NS_ABORT_MSG_UNLESS (iv.Get () == -3, "Cannot set ConfigExample‘s integer attribute via SetAttribute");

// These test objects are not rooted in any ns-3 configuration namespace.

// This is usually done automatically for ns3 nodes and channels, but

// we can establish a new root and anchor one of them there (note; we

// can‘t use two objects of the same type as roots). Rooting one of these

// is necessary for it to show up in the config namespace so that

// ConfigureAttributes() will work below.

Config::RegisterRootNamespaceObject (b_obj);

#ifdef HAVE_LIBXML2

// Output config store to XML format

Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.xml"));

Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));

Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Save"));

ConfigStore outputConfig;

outputConfig.ConfigureDefaults ();

outputConfig.ConfigureAttributes ();

#endif /* HAVE_LIBXML2 */

// Output config store to txt format

Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.txt"));

Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("RawText"));

Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Save"));

ConfigStore outputConfig2;

outputConfig2.ConfigureDefaults ();

outputConfig2.ConfigureAttributes ();

Simulator::Run ();

Simulator::Destroy ();

}

v Tracing系统

要跟踪程序运行信息:

l 用回调,回调C++的输出

#include <iostream>

...

int main ()

{

...

std::cout << "The value of x is " << x << std::endl;

...

}

l 用logging系统,可以有控制级别

如:

NS_LOG_LOGIC ("For me (interface broadcast address)");

l 用tracing系统

这是在NS3中最重要的部分。它依赖于ns3的回调和属性机制。示例在ns-allinone-3.19/ns-3.19/examples/tutorial/fourth.cc:

#include "ns3/object.h"

#include "ns3/uinteger.h“

//这个头文件引入要跟踪的数据

#include "ns3/traced-value.h"

//这个头文件包含了本程序要使用的能把自定义数据转化为Trace Source的函数

#include "ns3/trace-source-accessor.h"

#include <iostream>

using namespace ns3;

class MyObject : public Object

{

public:

static TypeId GetTypeId (void)

{

static TypeId tid = TypeId ("MyObject")

.SetParent (Object::GetTypeId ())

.AddConstructor<MyObject> ()

.AddTraceSource ("MyInteger",

"An integer value to trace.",

MakeTraceSourceAccessor (&MyObject::m_myInt))

;

return tid;

}

MyObject () {}

TracedValue<int32_t> m_myInt;

};

void

IntTrace (int32_t oldValue, int32_t newValue)

{

std::cout << "Traced " << oldValue << " to " << newValue << std::endl;

}

int

main (int argc, char *argv[])

{

Ptr<MyObject> myObject = CreateObject<MyObject> ();

//一般很少用,通常使用”config path”的子系统

myObject->TraceConnectWithoutContext ("MyInteger", MakeCallback (&IntTrace));

//

myObject->m_myInt = 1234;

}

运行结果:

clip_image012

使用config,在examples/tcp-large-transfer.cc中的部分片段:

void CwndTracer (uint32_t oldval, uint32_t newval) {}

...

Config::ConnectWithoutContext (

"/NodeList/0/$ns3::TcpL4Protocol/SocketList/0/CongestionWindow",

MakeCallback (&CwndTracer));

对参数说明:”/”代表后面紧跟命名空间,如果确定Configpath?只要进入API文档输入需要的类,会发现Config Paths标题,进去之后就知道了。

确定Trace Source:展开”Modules”->”C++ Construct Used by All Modules”,可以看到”the list of all trace source”找到可以使用的Trace Source。

确定Trace Sink:它其实是个函数,主要考虑返回值和参数。如果是Config::ConnectWithoutContext,那么函数可以只有一个变量:

Void courseChangeCallback(Ptr<const MobilityModel> model)

如果是Config::Connect,增加一个字符串参数:

Void courseChangeCallback(std::string path,Ptr<const MobilityModel> model)