在主从DC同步模式下,主站需要以非常精准的时间发送过程数据,如下图所示:
如下图所示,影响实时性的关键因素是操作系统和网卡驱动,前者需要将过程数据准时送出,后者需要优化网卡驱动,即"准时出发,路上不能耽误"。
操作系统的实时性体现在需要非常准时地调用EtherCAT主站协议栈的发送函数,例如SOEM的发送函数是ecx_send_processdata()
Etherlab的发送函数为ecrt_master_send()。
采用Linux系统时,需要打上实时补丁,如Xenomai、RTAI、OSADL等。
标准Linux下的网卡驱动是为通用的网络通信设计的,网络数据穿过TCP/IP协议栈是一个非常漫长的过程,其中包括各种安全性检查、路由、出入队列、分片和重组等等,这其中有很多的不确定性,所以在EtherCAT主站开发中需要优化网卡驱动,使EtherCAT数据
绕过TCP/IP协议栈。
在Etherlab的说明文档中给出了改造标准网卡驱动的三个基本点,如下:
(1)禁用netif_*()
(2)禁用中断
(3)重复利用socket buffer
接下来以EtherLAB源码中移植好的网卡驱动RealTek RTL-8139为例进行说明。
在网卡驱动程序中的接收函数rtl8139_rx()中,netif_receive_skb (skb)负责将数据包传递给TCP/IP协议栈,
将其改成由EtherCAT主站直接处理。
改造前:
//8139too-3.4-orig.c
static int rtl8139_rx(struct net_device *dev, struct rtl8139_private *tp,
int budget)
{
......
netif_receive_skb (skb);//将数据包提交给TCP/IP协议栈处理
......
}
改造后:
//8139too-3.4-ethercat.c
static int rtl8139_rx(struct net_device *dev, struct rtl8139_private *tp,
int budget)
{
......
if (tp->ecdev) { //作为EtherCAT使用时直接交给EtherCAT协议栈处理,绕过TCP/IP协议栈
ecdev_receive(tp->ecdev,
&rx_ring[ring_offset + 4], pkt_size);
dev->last_rx = jiffies;
dev->stats.rx_bytes += pkt_size;
dev->stats.rx_packets++;
}
else
{
......
netif_receive_skb (skb);
......
}
}
Linux标准网卡驱动中,采用中断方式收发网络数据包,较新的网卡驱动中采用NAPI(中断和轮询相结合),而EtherCAT通信中,EtherCAT主站发出过程数据包后,主站非常清楚数据包什么时候返回主站,因此不需要采用中断的方式,而由主站直接查询和处理返回的过程数据包。
改造前:
//8139too-3.4-orig.c
static int rtl8139_open (struct net_device *dev)
{
......
retval = request_irq (dev->irq, rtl8139_interrupt, IRQF_SHARED, dev->name, dev); //向内核注册中断
......
}
改造后:
//8139too-3.4-ethercat.c
static int rtl8139_open (struct net_device *dev)
{
......
if (!tp->ecdev) {
retval = request_irq(dev->irq, rtl8139_interrupt, //作为普通网卡时才注册中断
IRQF_SHARED, dev->name, dev);
if (retval)
return retval;
}
......
}
void ec_poll(struct net_device *dev)
{
rtl8139_interrupt(0, dev);
}
函数调用关系为:ec_poll()->rtl8139_interrupt()->rtl8139_rx()->ecdev_receive();
Linux标准网卡驱动中,将数据包发送后将释放数据包所占用的内存,或者放回预先分配的内存池中,
而EtherCAT通信中,只需重复使用其中的一两个缓存即可,这样可以节省为数据包分配和释放内存的时间。
改造前:
//8139too-3.4-orig.c
static netdev_tx_t rtl8139_start_xmit (struct sk_buff *skb,
struct net_device *dev)
{
......
dev_kfree_skb(skb);//释放Socket buffer所占内存或将其放回内存池
......
}
改造后:
//8139too-3.4-ethercat.c
static netdev_tx_t rtl8139_start_xmit (struct sk_buff *skb,
struct net_device *dev)
{
......
if (!tp->ecdev) { //只有作为普通网卡时才释放Socket buffer
dev_kfree_skb(skb);
}
......
}