OPNET学习笔记之eth_mac_intf模块(ethcoax_station_adv节点模型)
前面已经描述了ethcoax_station_adv节点模型的基本结构,其中burstry_gen模块产生无格式包,发送给eth_mac_intf模块处理,本文分析其处理过程。
首先注意该模块上的包流:
stream : eth_mac_intf [0] -> sink [1]
stream : eth_mac_intf [1] -> mac [1]
stream : bursty_gen [0] -> eth_mac_intf [1]
stream : mac [1] -> eth_mac_intf [0]
该模块的进程模型为ethernet_mac_interface,状态机如下:
该模块没有local/global statistic,但是有两个attribute,分别为high dest address和low dest address,表示dest address的最大值和最小值。状态机中只有两个强制状态,其它为非强制状态。
一,init状态的入口代码:
/* This function initializes its state variables and registers itself in the process registry */
eth_mac_higher_layer_intf_init (); //调用该函数初始化,包括初始化状态变量 和注册进程
/* Set up an interface control information (ICI) structure to */
/* communicate parameters to the MAC layer process. We will */
/* leave this ICI installed for the entire simulation. */
intf_mac_req_iciptr = op_ici_create ("mac_request"); //创建一个ICI结构体,以便与MAC层通信,注意返回值类型为Ici*,而"mac_request"是预定义好的结构体(在哪里定义???)(syntax:op_ici_create(fmt_name);)
op_ici_install (intf_mac_req_iciptr); //绑定????,这个ICI被自动绑定到被唤醒进程向外发送的中断,注意ICI指针可以由op_ici_create()和op_intrpt_ici()返回,其中前者返回一个新创建的ICI(在ICI发送进程),后者返回一个与即将到来的中断关联的ICI(在ICI接收进程)。
/* Schedules a self interrrupt */
op_intrpt_schedule_self (op_sim_time (), 0); //自中断,时间为当前时间
二,分析该模块的初始化函数eth_mac_higher_layer_intf_init ():
static void eth_mac_higher_layer_intf_init ()
{
/** Initializes the state variables for this process model and registers itself in the model-wide reigtry */
FIN (eth_mac_higher_layer_intf_init ());
/* Gets its own id */
my_id = op_id_self (); //获得自己模块的objid。
/* Gets the object id of the node to which this module belongs */
own_node_objid = op_topo_parent (my_id); //获得自己模块所在节点的objid。
/* Gets the objid of its subnet */
subnet_objid = op_topo_parent (own_node_objid); //获得自己模块所在节点的子网的objid。
/* Gets a handle to its process */
own_prohandle = op_pro_self (); //获得当前运行进程的句柄,有何用处???(可以获取进程模型的各种属性)
/* Gets the name of its process model */
op_ima_obj_attr_get (my_id, "process model", proc_model_name); //通过objid获得当前进程模型的process model属性,送到proc_model_name,注意proc_model_name是char[20]。
/* Gets a process record handle to record its process in the registry */
//在model registry(进程登记库)中注册进程,并返回handle,注意model registry是进程注册的一个表,通过返回的handle操作,可以注册、查询、设置等。
//model registry的目的是在各层协议模块间共享数据,具体见《大解密》P286,
//进程登记库的目的是:(1)为仿真中所有进程提供全局的信息注册机制,(2)允许进程在节点内共享信息,进程可以设置多种类型的属性(3)进程注册后,可以用多种查询参数找到并访问其公开信息。相关文件为oms_pr.h和oms_pr.ex.c
own_process_record_handle = (OmsT_Pr_Handle) oms_pr_process_register (own_node_objid, my_id,
own_prohandle, proc_model_name); //注意返回值类型是OmsT_Pr_Handle
/* Sets the required attributes in the process registry */
//利用返回的handle,设置进程在process registry的属性
oms_pr_attr_set (own_process_record_handle, "location", OMSC_PR_STRING, "mac_if",
"subnet", OMSC_PR_OBJID, subnet_objid, OPC_NIL); //syntax:oms_pr_attr_set(pr_handle,attr0_name,attr0_type,attr0_value,……,OPC_NIL);,最后一个常量表示结束。登记了两个属性,分别为location 和subnet
/* Stream numbers to the mac layer and from the mac layer which */
/* which will be set in the exit execs of the wait state */
//进出的stream index,这两个量会在wait状态的出口代码中被赋值,这里被设置为无效值。
outstrm_to_mac = OPC_INT_UNDEF;
instrm_from_mac = OPC_INT_UNDEF;
FOUT;
}
三,init2状态的入口代码:
/* Schedule a self interrupt to wait for lower layer Ethernet MAC process*/
/* to initialize and register itself in the model-wide process registry. */
op_intrpt_schedule_self (op_sim_time (), 0); //自中断,时间为当前时间,目的是等待底层的MAC进程初始化并注册,目的是用于一些系统的初始化配置,比如你的网络开始运行之前,你需要为每一个节点分配适当的地址,并且你必须保证每一个节点配置了初始节点网络才能够开始运行。这就是用两个unforced init模块模块的原因。
四,wait状态的入口代码:同上。
出口代码:(要从节点内的进程登记库中提取MAC的流信息,所以必须等待所有底层的进程完成初始化才开始)
/* Obtain the MAC layer information for the local MAC process from the model-wide registry. */
proc_record_handle_list_ptr = op_prg_list_create (); //功能是分配一个新的空表
oms_pr_process_discover (my_id, proc_record_handle_list_ptr, "node objid", OMSC_PR_OBJID, own_node_objid,
"protocol", OMSC_PR_STRING, "mac",OPC_NIL); //在进程登记库中发现符合条件的进程,这里是本节点内部的协议为“mac”的进程,(syntax:oms_pr_process_discover(neighber_objid,pr_handle_lptr, attr0_name, attr0_type, attr0_value,...., OPC_NIL))
/* If the MAC process registered itself, then there must be a valid match*/
record_handle_list_size = op_prg_list_size (proc_record_handle_list_ptr); //判断是否是正好一个match的进程
if (record_handle_list_size != 1)
{
/* An error should be created if there are more than one Ethernet-MAC process in the local node, or if no match is found. */
op_sim_end ("Error: either zero or several Ethernet MAC processes found in the interface", "", "", ""); //多于一个,出错了
}
else
{
/* Obtain a handle on the process record. */ //正确,只有一个,通过在进程登记库中的位置,获得MAC进程的handle,并送到process_record_handle
process_record_handle = (OmsT_Pr_Handle) op_prg_list_access (proc_record_handle_list_ptr, OPC_LISTPOS_HEAD);
/* Obtain the module objid for the Ethernet MAC module. */ //获得MAC模块的objid并记录到变量mac_module_objid中,注意mac模块应登记了objid
oms_pr_attr_get (process_record_handle, "module objid", OMSC_PR_OBJID, &mac_module_objid);
//pr就是process record
/* Obtain the address handle maintained by the MAC process. */ //获得MAC模块的属性,包括mac地址和auto address handle???
oms_pr_attr_get (process_record_handle, "address", OMSC_PR_INT64, &ne_address);
oms_pr_attr_get (process_record_handle, "auto address handle", OMSC_PR_POINTER, &oms_aa_handle);
/* Set the variable to indicate the MAC address of the associated MAC layer process.*/
mac_address = ne_address; //得到mac地址
/* Obtain the stream numbers connected to and from the Ethernet MAC layer process. */
//获得MAC模块连接的stream index,并送到instrm_from_mac和outstrm_to_mac。
oms_tan_neighbor_streams_find (my_id, mac_module_objid, &instrm_from_mac, &outstrm_to_mac);
}
//---------------------------------------------------------------------------下面是获取目的地址,可以是设定或随机选择的
if (oms_aa_handle == OPC_NIL)
{
/* This is a unconnected node. Do nothing */
}
else
{
/* Determine address range for destination assignment. */
if ((op_ima_obj_attr_get (my_id, "low dest address", &low_dest_addr) == OPC_COMPCODE_FAILURE) || //从属性中获取地址范围
(op_ima_obj_attr_get (my_id, "high dest address", &high_dest_addr) == OPC_COMPCODE_FAILURE))
{
eth_mac_higher_layer_intf_error ("Unable to read destination address range.", OPC_NIL, OPC_NIL);
}
/* If any single address is specified as broadcast, indicate this as an error */
/* Both the low and high destination addresses together can be specified as broadcast */ //注意-1是广播地址???
if (((low_dest_addr == -1) && (high_dest_addr != -1)) ||
((high_dest_addr == -1) && (low_dest_addr != -1)))
{
eth_mac_higher_layer_intf_error ("Both the Lowest and Highest Destination addresses must be specified as Broadcast", "Any one attribute cannot be Broadcast", OPC_NIL);
}
/* Check whether highest destination address is not lower than the lowest destination */
/* address (also check it is not a broadcast address or MAX_DEST_ADDR). */
if ((high_dest_addr < low_dest_addr) && (high_dest_addr >= 0))
{
eth_mac_higher_layer_intf_warn ("Specified lowest destination address is greater than specified highest destination address.",
"Setting the lowest destination address to Minimum Dest Address, and the highest to Maximum Dest Address.", OPC_NIL);
/* If so, then set the lowest destination address to the minimum destination address */
/* and the highest destination address to the highest destination address. */
low_dest_addr = MIN_DEST_ADDR;
high_dest_addr = MAX_DEST_ADDR;
}
/* Determine our destination or destination range. Start by initializing the range indices, which are used if a range is specified. */
//确定目的地址的range
low_dest_index = OMSC_AA_UNINIT_ADDR;
high_dest_index = OMSC_AA_UNINIT_ADDR;
if ((low_dest_addr == MIN_DEST_ADDR) && (high_dest_addr == MAX_DEST_ADDR)) //前面判断地址范围有问题时
{
/* We randomly pick our destination from universal address set. */
destination_address = OMSC_AA_AUTO_ASSIGN;
}
else if (low_dest_addr == high_dest_addr)
{
/* We have a unique destination address specified. Check its validity. */
if (low_dest_addr == -1)
destination_address = OMSC_AA_BROADCAST;
else if (oms_aa_dest_addr_check (oms_aa_handle, low_dest_addr) == OPC_TRUE)
destination_address = low_dest_addr; //正确的唯一地址
else
{
sprintf (err_msg, "%d", low_dest_addr);
eth_mac_higher_layer_intf_error ("Invalid destination MAC address.", "No MAC was found with the address:",err_msg);
}
}
else
{
/* We randomly pick our destination addresses from a subset of the */
/* universal address set. Determine the lower and upper indices of this */
/* subset in the global address array. */
if (low_dest_addr == MIN_DEST_ADDR)
low_dest_index = 0;
else
{
low_dest_index = oms_aa_address_find (oms_aa_handle, low_dest_addr);
/* Check validity of the lower bound. */
if (low_dest_index < 0)
{
sprintf (err_msg, "%d", low_dest_addr);
eth_mac_higher_layer_intf_error ("Invalid destination MAC address.", "No MAC was found with the address:", err_msg);
}
else if (oms_aa_dest_addr_check (oms_aa_handle, low_dest_addr) == OPC_FALSE)
{
sprintf (err_msg, "MAC address specified as the lowest destination address (%d)", low_dest_addr);
eth_mac_higher_layer_intf_error (err_msg, "is not configured as a valid destination address.",
"Most probably that MAC belongs to a switch or bridge node.");
}
}
/* Now determine the upper bound. */
if (high_dest_addr == MAX_DEST_ADDR)
high_dest_index = oms_aa_max_addr_index_get (oms_aa_handle);
else
{
high_dest_index = oms_aa_address_find (oms_aa_handle, high_dest_addr);
/* Check validity of the upper bound. */
if (high_dest_index < 0)
{
sprintf (err_msg, "%d", high_dest_addr);
eth_mac_higher_layer_intf_error ("Invalid destination MAC address.", "No MAC was found with the address:", err_msg);
}
else if (oms_aa_dest_addr_check (oms_aa_handle, high_dest_addr) == OPC_FALSE)
{
sprintf (err_msg, "MAC address specified as the highest destination address (%d)", high_dest_addr);
eth_mac_higher_layer_intf_error (err_msg, "is not configured as a valid destination address.",
"Most probably that MAC belongs to a switch or bridge node.");
}
}
}
}
//-----------------------------------------------------地址完成
五,idle状态的入口代码为空,当收到中断时,根据条件执行出口代码:
注意在header block中定义的条件:当stream index是从mac来时为MAC中断,其它为应用层中断。
/* Definition for the transition conditions */
#define MAC_LAYER_PKT_ARRVL (intrpt_type == OPC_INTRPT_STRM && intrpt_strm == instrm_from_mac)
#define APPL_LAYER_PKT_ARRVL (intrpt_type == OPC_INTRPT_STRM && intrpt_strm != instrm_from_mac)
首先执行出口代码:
/* The only interrupt expected in this state is a stream interrupt. It can be either from the MAC*/
/* layer for a packet destined for this node or from the application layer for a packet destined for some other node.*/
intrpt_type = op_intrpt_type (); //首先得到中断类型和stream index
intrpt_strm = op_intrpt_strm ();
/*Get the packet from the appropriate stream */
pkptr = op_pk_get (intrpt_strm); //得到包指针,注意由于包需要在不同的状态处理,所以pkpt是state variable,始终有效
五,appl layer arrival状态,由于该状态是强制状态,执行入口代码后马上跳回idle:这里只是分配了mac地址和ICI信息。
/* A packet has arrived from the application layer. Pick a random */
/* destination, unless an explicit destination is specified. Don't try infinite times.*/
num_tries = 0;
do
{
/* Increment the try counter. This will prevent an infinite loop. */ //不能无穷的尝试发送
num_tries++;
/* Pick a random destination address unless the destination address is already set to a specific value. */
if (low_dest_index + high_dest_index > 0) //地址没有设定,可能的最高加最低地址大于零,即非广播地址,
{
oms_aa_dest_addr_from_range_get (oms_aa_handle, &integer_mac_address, low_dest_index, high_dest_index); //用该函数随机获取地址范围内的一个地址,并送到integer_mac_address。
curr_dest_addr = integer_mac_address;
}
else
{
integer_mac_address = destination_address; //地址已经确定
oms_aa_dest_addr_get (oms_aa_handle, &integer_mac_address); //获取一个随机地址从address list
curr_dest_addr = integer_mac_address;
}
} while ((curr_dest_addr == mac_address) && (num_tries < TRY_THRESH)); //目的地址不能是自己,也不能无限循环
if (curr_dest_addr == mac_address)
eth_mac_higher_layer_intf_error ("Unable to choose remote address.", OPC_NIL, OPC_NIL);
if (curr_dest_addr == OMSC_AA_UNINIT_ADDR)
{
op_pk_destroy (pkptr);
}
else
{
/* Set this information in the interface control information to be sent to the MAC layer. */
op_ici_attr_set_int64 (intf_mac_req_iciptr, "dest_addr", curr_dest_addr); //向MAC层提交ICI信息,内容是mac地址
/* Send the packet to the lower layer */
op_pk_send (pkptr, outstrm_to_mac); //发送包,注意op_pk_send()函数会默认销毁包
}
六,mac layer arr状态,对于mac层的包,直接发送给应用层,由于本模型应用层是sink模块,所以直接发送即可。
/* A packet arrived from the MAC layer. Since the MAC layer would have forwarded this only if it were */
/* destined for this node, forward this packet to the application layer. */
op_pk_send (pkptr, 0); //默认的stream index为0的是连接到sink模块
总结:eth_mac_intf模块是在应用层与mac层之间的接口,功能是(1)从上层传递的包,随机(或指定)一个mac地址,然后发送给mac层,其中地址作为ICI信息发送。(2)mac层传过来的包直接发给上层,因为上层是sink模块,直接把包销毁,所以不需要做其它的工作了。
注意的:(1)自中断等待其它进程完成初始化。(2)进程登记库及相关函数(pr),用于得到本节点内其它模块的属性,共享信息。(3)用ICI在层间传递信息,这里是传递mac地址到mac层。(4)自动分配地址,oms_aa_dest_addr_from_range_get()系列函数。