# 简介与配置说明

### 概述

使用以太网时, 通信不是确定性的, 因为冲突, 这可能发生在具有集线器的网络上的多个主机之间, 或者因为 switch 情况下的未知延迟。

以太网协议不允许确定性通信因为可能会发生冲突并支持通过机制 CSMA/CD (载波侦听多址访问/冲突检测)。

`RTnet` 是一个协议栈, 运行在以太网层和应用层(或 IP 层)。它的目标是, 通过使用时间 `intervals`(时隙), 是为了进行确定性的通信, 通过禁用冲突检测CSMA/CD, 并防止缓冲数据包在网络中。`RTnet` 是为在 `Linux` 内核上运行而开发的软件使用 `RTAI` 或 `xkernel` 实时扩展。它利用实时 `kernel` 扩展来确保通信堆栈上的确定性。为此, 与此协议相关的所有说明都使用实时内核函数, 而不是 `Linux` 的内核函数, 后者将执行时间的延迟和中断的延迟, 其中提供确定的通信。

### 同步

`Rtnet` 使用时分多路复用 `TDMA`(时分倍数Access) 以避免通信冲突, 主服务器在每个循环的开始, 一个包含来自要同步的全局时钟。发送同步帧并包含编码为 4 的周期编号 `bytes`, `master` 的参考时间值(以纳秒为单位)和进行传输时的参考时间的值, 则 `value` 有助于减少全局时钟的偏差。这同步消息包含以下数据：为了弥补缺失集中式主从, 二级主服务器 (backup-master) 可以是通过为 `backup master` 预订额外的 `slot` 来建立。如果 `primary master` 倒下, `secondary master` 在服用考虑与上一帧同步的偏移。当 `primary master` 重新投入服务, 它与活动的 `secondary master`, 则 `secondary master` 被禁用。

### 协议栈 (仅支持 raw socket, 暂不支持 UDP/TCP)

`Rtnet` 协议栈包含一个物理部分到传输层 `OSI` 模型。它是专门为实时工作而开发的内核 `RTAI` 和 `xkernel`。确保通信的确定性应用程序, 以太网控制器应用程序软件使用的所有功能都满足硬性实时约束。因此, 网络层协议和传输尤其必须实现以使用 `Linux` 的实时内核, 而不是常用的 `Linux` 内核。

### 物理层

物理层由以太网控制器支持, 需要使用来自 `Linux` 的特定驱动程序。处理内存、中断处理机制或屏蔽中断需要由实时内核执行的操作, 以确保确定性。在以太网驱动程序中, 应尽可能精确地检测发射和接收的瞬间。从材料角度来看, 可以使用任何以太网控制器, 并可以支持其他物理层(如Firewire)。管理控制器以非实时方式执行初始化、设置和关闭操作, 因此应特别注意传输和接收功能。在传输时, 应保存全局时钟的值, 而在接收函数中, 应在接收时保存全局时钟的值, 以便精确控制抖动。

### 数据链路层

以太网作为标准操作不能提供硬实时通信, 无论是在基于中继器的网络上, 还是在这有碰撞的风险。提供的服务质量 IEEE 802.1q 为此问题提供了一个很好的解决方案, 但是交换机的集中化布线成本高昂。数据链路协议的 `Rtnet` 由名为 `Rtmac` 的内核模块提供, 其中包含一个模块 `TDMA`。`TDMA` 模块管理网络上的通信(同步、循环时间等)虽然此层是可选的堆栈 `Rtnet` 的扩展, 它带来了确定性的媒体访问协议：

- 将特定的数据包转发到适当的服务
- 用更高层协议交换控制消息和数据
- 在使用多个网络接口的情况下, 可以定义媒体访问控制特定接口
- 服务隧道与非实时网络交换数据

`Rtnet` 的 `Rtmac` 层是一个提供以下服务的模块：

- 截取特定的包信息, 将其重定向到适当的服务
- 在上层并行数据应用程序中交换控制数据或特定信息
- 建立隧道, 通过非实时系统进行通信
- 使用一个虚拟接口和添加一个头来区分这个流

### TDMA 层

`Rtnet` 在数据链路层使用时分接入网络, 以防止发生碰撞。该协议同步该网段使用主从通信, 并定义数据传输到传输的同步消息的时间周期性地。如果一个节点在启动时也可以加入网络, 如果他知道他的时间访问权, 在节点或服务器上手动配置在网络节点上就地配置 (rtcfg)。一旦此参数获取的 `Node` 将通过以下方式评估返回网络的时间向主同步发送查询, 主同步将通过以下方式响应在数据包发送的计时信息中包括可以计算返回时间。

### 网络和传输层

UDP/IP 和 TCP/IP 网络和传输层的堆栈支持, 通过简化路由和优化的过程碎片。ARP (Address Resolution Protocol) 协议适用于仅在初始化时操作。在通讯方面对于总线来说, 这些协议可能是不必要的, 但却是相关的与外部网络的通信, 通常是非实时的。`IP` 协议实现运行在实时内核和支持分片发送超过 MTU (Maximum . MTU)的报文传输设备。要执行路由, 需要使用两个表, 第一个表表中包含主机的 `IP` 地址, 即来宾路由表在局域网内可达。第二个表(可选)使用网关地址到其他网络的实时, 允许建立具有更复杂结构的网络。路由表的内容类似于标准 `IP` 栈的 `ARP` 表。对于 `Rtnet` 来说表用于路由过程, 但情况并非一定如此用于标准电池。UDP 和 TCP 在 IP 层之上实现(版本4), `UDP` 用于硬实时传输。其次是 `TCP` 最近实施的目的是与要素进行沟通在不运行 `Rtnet` 的网络中, `TCP` 要复杂得多(在100多个rfc中有描述)。因此它的实施已大大简化, 因为它的用途是有限的。`UDP` 协议部分, 使用默认协议建立实时通信在两个应用程序之间。`Rtcfg` 是为 `Rtnet` 定义的协议有效配置网络。为此, 使用网络节点作为并向其他节点(客户端)发送配置信息。由于虚拟接口的存在, 可以运行所有网络层协议, 并在更高标准的 `Linux` 上实现内核。对于有限的通信网络节点也是可行的不使用路由协议和传输(套接字类型`RAW`)。

### 配置说明

注意在加载 `rtnet` 驱动时, 需先对 `rtnet` 做如下配置

对配置文件进行简要说明, 解释如下

配置选项 | 描述
--- | ---
RT_DRIVER | rtnet 驱动名称
RT_DRIVER_OPTIONS | rtnet 驱动索引
REBIND_RT_NICS | rtnet 驱动设备
IPADDR | rtnet 驱动加载后的静态 ip
NETMASK | rtnet 驱动加载后的掩码

配置文件路径为 /usr/xenomai/etc/rtnet.conf (可通过 `vim` 编辑器对配置进行修改)

    # RT-NIC driver
    RT_DRIVER="rt_igb"
    RT_DRIVER_OPTIONS="1"
    REBIND_RT_NICS="0000:01:00.2" # 此项为支持 rt_igb 的 pci 设备的具体位置信息

    IPADDR="192.168.1.244"  # 此项需根据实际网络环境进行配置
    NETMASK="255.255.255.255" # 此项需根据实际网络环境进行配置

    #TDMA_MODE="master"
    #TDMA_SLAVES="127.0.0.1"
    #TDMA_CYCLE="5000"
    #TDMA_CONFIG="${prefix}/etc/tdma.conf"

对必要配置选项针对不同设备, ip 网段配置完成后, 取消 TDMA 相关配置(在配置文件中注释掉 TDMA 相关选项即可)

    # 加载 rtnet 驱动
    rtnet start
    # 卸载 rtnet 驱动
    rtnet stop
    # 类似 ifconfig 命令, 显示 rtnet 下配置的网口信息
    rtifconfig --help
    # Usage:
    #        rtifconfig [-a] [<dev>]
    #        rtifconfig <dev> up [<addr> [netmask <mask>]] [hw <HW> <address>] [[-]promisc]
    #        rtifconfig <dev> down
    # example:
    #        此项示例以 ip : 192.168.1.244 仅作为示例说明, 实际应用需根据实际网络环境进行配置
    #        启用 rteth0 网卡并将网卡 ip 以及 netmask 配置如下
    #		 rtifconfig rteth0 up 192.168.1.244 netmask 255.255.255.255
    #        禁用 rteth0 网卡
    # 		 rtifconfig rteth0 down

    # 类似 route 命令, 显示 rtnet 下配置的路由信息
    rtroute --help
    #Usage:
    #        rtroute
    #        rtroute solicit <addr> dev <dev>
    #        rtroute add <addr> <hwaddr> dev <dev>
    #        rtroute add <addr> netmask <mask> gw <gw-addr>
    #        rtroute del <addr> [dev <dev>]
    #        rtroute del <addr> netmask <mask>
    #        rtroute get <addr> [dev <dev>]
    #        rtroute -f <host-routes-file>
    # example:
    #        此项示例以 ip : 192.168.1.244, mac : C8:6B:BC:80:08:EE 仅作为示例说明, 实际应用需根据实际网络环境进行配置
    #		 向静态路由表中添加如下路由配置信息：
    #        rtroute solicit 192.168.1.244 dev rteth0
    #        rtroute add 192.168.1.244 C8:6B:BC:80:08:EE dev rteth0
    rtping 192.168.1.244

用户可根据自己需要通过 rtifconfig 进行网口的 ip 、netmask、 up、down 等或 rtroute 进行路由操作, 如 add、del、get 等

### 配套使用示例程序简要说明

详情请查阅 RTnet 示例程序说明

#### 具体通信流程图如下

##### Server

    flowchart TD
        Start[开始]
        services_init[服务器初始化]
        create_sub_rt_thread[创建实时子线程]
        ctrl_c_or_system_error[退出信号或系统报错]
        send_data[发送数据包]
        recv_data[接收数据包]
        filter_data[过滤数据包]
        End[结束并退出]

        Start --> services_init --> create_sub_rt_thread --> ctrl_c_or_system_error
        ctrl_c_or_system_error --> |是|End
        ctrl_c_or_system_error --> |否|recv_data --> filter_data
        filter_data --> |是|send_data --> ctrl_c_or_system_error
        filter_data --> |否|ctrl_c_or_system_error

##### Client

    flowchart TD
        Start[开始]
        client_init[客户端初始化]
        create_sub_rt_thread[创建实时子线程]
        ctrl_c_or_system_error[退出信号或系统报错]
        send_data[发送数据包]
        recv_data[接收数据包]
        filter_data[过滤数据包]
        parse_data[解析数据包]
        End[结束并退出]

        Start --> client_init --> create_sub_rt_thread --> ctrl_c_or_system_error
        ctrl_c_or_system_error --> |是|End
        ctrl_c_or_system_error --> |否|send_data
        send_data --> recv_data --> filter_data
        filter_data --> |是|parse_data --> ctrl_c_or_system_error
        filter_data --> |否|recv_data

### 实时性对比

当固定发送定长数据至网卡系统平均耗时即以相关数据统计分析

| 平均耗时/ns | 标准差 | 变异系数
--- | --- | --- | ---
rtnet | 45620.72 | 1421.41 | 0.0311571
no_rtnet | 79528.26 | 11009.68 | 0.1384373

此项数据对比表明, 当使用 `rtnet` 驱动操作实时网卡时, 发送相同数据至网卡的平均耗时较低, 同时每次耗时较为接近, 标准差偏离较小, 耗时稳定, 而使用 `no_rtnet` 驱动时, 平均耗时较高, 而且波动相对较大, 发送时长不稳定