EtherNet/IP I/O 丢包排查(linux系统)

EtherNet/IP I/O 丢包排查


一、先确认丢包现象

1.1 从应用层确认

复制代码
# 查看 EtherNet/IP 主站/从站应用的日志或状态
# 常见表现:连接超时断开、数据不更新、ForwardOpen 失败重连
  • 检查 EtherNet/IP 协议栈是否打印了超时(Timeout)或连接丢失(Connection Loss)日志
  • 确认是偶发丢包 还是持续丢包,间隔是否有规律

1.2 确认是 I/O(隐式)丢包还是显式丢包

丢包类型 传输层 端口 表现
I/O 隐式丢包 UDP 2222 数据跳变、更新滞后、连接超时
显式丢包 TCP 44818 请求无响应、配置失败

I/O 隐式报文走 UDP,天然不保证可靠送达,丢包排查重点在 UDP 层。


二、逐层排查(自底向上)

第 1 层:物理链路

bash 复制代码
# 查看以太网接口状态
ip link show
# 关注: state UP/DOWN, NO-CARRIER

# 查看接口错误统计
ip -s link show eth0
# 关注: errors, dropped, overruns, carrier

# 查看详细以太网统计
ethtool -S eth0
# 关注: rx_crc_errors, rx_missed_errors, tx_errors, rx_fifo_errors, tx_fifo_errors
#       rx_long_length_errors, rx_short_length_errors, rx_align_errors

# 检查双工和速率是否匹配
ethtool eth0
# Speed 和 Duplex 必须与交换机端口一致(Full duplex)

# 检查网线/光模块
ethtool -m eth0   # 光模块诊断信息(SFP/QSFP)

常见物理层问题:

  • 网线质量差、过长(>100m 铜缆)
  • 双工不匹配(半双工 ↔ 全双工)→ CRC 错误激增
  • EMI/电磁干扰(强电电缆与网线平行走线)
  • 交换机端口损坏或缓冲区溢出

第 2 层:MAC / 数据链路层

bash 复制代码
# 查看 ARP 表
ip neigh show
# 关注: REACHABLE vs STALE vs FAILED

# 实时监控 ARP 变化
watch -n 1 'ip neigh show'

# 抓包看 MAC 层重传
tcpdump -i eth0 ether proto 0x0800 -v -c 1000
# 关注是否有大量 ARP 请求(IP 冲突可能)

常见链路层问题:

  • 目标设备 IP 与 MAC 映射失效(ARP 表老化)
  • 网络中存在 IP 冲突
  • 交换机 MAC 表溢出导致泛洪

第 3 层:IP / 网络层

bash 复制代码
# 系统层面网络错误统计
netstat -s
# 关注:
#   - UDP: receive errors, receive buffer errors, packet receive errors
#   - IP: fragments failed, reassembly failures

# 查看所有接口的网络统计
cat /proc/net/dev
# 关注各接口的 drop 和 error 列

# 查看 UDP 协议详细统计
cat /proc/net/udp
cat /proc/net/snmp | grep Udp
# 关注: InErrors, RcvbufErrors, InCsumErrors, InDatagrams vs OutDatagrams

# 网络接口丢包快照(持续监控)
watch -n 1 'cat /proc/net/dev | grep eth0'

UDP 丢包关键指标:

bash 复制代码
netstat -su

重点关注:

  • packet receive errors --- 接收缓冲区满导致的丢包
  • receive buffer errors --- 内核 UDP 接收缓冲区溢出
  • packets to unknown port received --- 端口未监听就收到包

第 4 层:UDP 缓冲区 / 内核层面

这是 EtherNet/IP I/O 丢包最常见的原因之一。

bash 复制代码
# 查看当前 UDP 缓冲区大小
sysctl net.core.rmem_default    # 默认接收缓冲区
sysctl net.core.rmem_max        # 最大接收缓冲区
sysctl net.core.wmem_default
sysctl net.core.wmem_max

# 查看当前实际使用情况
cat /proc/net/udp
# 列: sl  local_address rem_address   st tx_queue rx_queue ...
# rx_queue > 0 说明应用来不及取包

# 查看 socket 级别的丢包
ss -u -a
# 关注 Recv-Q 列(接收队列积压)

# 实时监控 socket 接收队列
watch -n 0.5 'ss -u -a | grep 2222'

调优建议:

bash 复制代码
# 增大 UDP 接收缓冲区(临时生效)
sysctl -w net.core.rmem_max=16777216     # 16MB
sysctl -w net.core.rmem_default=2097152  # 2MB

# 或直接针对 socket 设置大缓冲区(应用层面,代码中设置 SO_RCVBUF)
# int bufsize = 2 * 1024 * 1024;      // 2MB
# setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize));

三、抓包分析(核心手段)

3.1 使用 tcpdump 抓取 I/O 数据流

bash 复制代码
# 抓取 EtherNet/IP I/O 流量(UDP 端口 2222)
tcpdump -i eth0 udp port 2222 -w eip_io.pcap -v
# 运行一段时间后 Ctrl+C 停止

# 实时查看 I/O 包
tcpdump -i eth0 udp port 2222 -nn -v
# 关注: 发包时间间隔是否均匀,是否有长时间无包的间隔

# 统计每秒 I/O 包数量(与 RPI 对比)
tcpdump -i eth0 udp port 2222 -nn | pv -l -i 1 > /dev/null

3.2 用 Wireshark 离线分析

bash 复制代码
# 在 Linux 上抓,复制到有图形界面的机器用 Wireshark 打开
# 或直接使用 tshark(Wireshark 命令行版)
tshark -r eip_io.pcap -Y "cip" -T fields \
  -e frame.time_relative \
  -e cip.connid \
  -e cip.seqcount

3.3 关键分析维度

用 Wireshark 打开 pcap 后,按以下维度分析:

分析项 Wireshark 操作 需关注的异常
时间间隔 Statistics → IO Graph 间隔突增 > 2×RPI
序列号连续性 过滤 cip.connid==xxx,查看 seqcount 序列号跳变/不连续
Connection ID 过滤 cip.connid 是否有异常的 CID 变化
包大小 统计 Length 字段 长度是否与预期 Assembly 数据大小一致
RPI 准确性 计算相邻包时间差 实际间隔是否偏离设定的 RPI
连接状态 过滤 cip.service == 0x4E 是否有意外 ForwardClose

Wireshark IO Graph 示例配置(在 Linux 上可用 tshark 替代):

bash 复制代码
# 统计每 100ms 的 I/O 包数量
tshark -r eip_io.pcap -Y "udp.port == 2222" -T fields \
  -e frame.time_relative | awk '{
    bucket=int($1*10);
    count[bucket]++
  } END {
    for(b in count) print b, count[b]
  }' | sort -n

# 检查序列号连续性
tshark -r eip_io.pcap -Y "cip" -T fields \
  -e frame.number -e cip.connid -e cip.seqcount | head -100

四、系统层面排查

4.1 CPU 与实时性

bash 复制代码
# 查看 CPU 使用率,关注是否有 CPU 核被打满
top -H
# 关注 EIP 应用线程的 CPU 占用和状态

# 查看中断分布
cat /proc/interrupts | grep eth
# 网卡中断是否集中在单核 → 可能导致 IRQ 风暴丢包

# 查看软中断
cat /proc/softirqs
# NET_RX 和 NET_TX 统计是否异常增长

# 查看进程上下文切换
pidstat -w -p <EIP_PID> 1
# 过高说明频繁切换,影响实时性

4.2 中断与 CPU 亲和性

bash 复制代码
# 查看网卡中断号
ethtool -x eth0          # 查看 RSS 队列分布
grep eth0 /proc/interrupts  # 查看中断在各 CPU 上的分布

# 将网卡中断分散到多核(减少单核瓶颈)
# 通过 irqbalance 自动均衡:
systemctl status irqbalance

# 或手动设置中断亲和性:
echo f > /proc/irq/<IRQ_NUM>/smp_affinity    # 绑定到 CPU 0-3

4.3 内核调度策略

bash 复制代码
# 查看应用线程调度策略
chrt -p <TID>

# 如果 EIP 应用需要实时性,设置为 FIFO 调度
chrt -f -p 50 <TID>     # SCHED_FIFO, 优先级 50

# 设置实时优先级需 root 权限
# 永久生效需配置 /etc/security/limits.conf:
# @realtime - rtprio 99

4.4 网络协议栈调优汇总

bash 复制代码
# ============= /etc/sysctl.conf 推荐配置 =============

# UDP 缓冲区
net.core.rmem_max = 16777216
net.core.rmem_default = 2097152
net.core.wmem_max = 16777216
net.core.wmem_default = 2097152

# 网络设备队列(避免 NIC ring buffer 满导致丢包)
net.core.netdev_max_backlog = 5000
net.core.netdev_budget = 600

# 减少 UDP 校验和开销(对本地可靠网络)
# 注意:仅在信任网络质量时启用
# net.ipv4.udp_checksum = 0

# 禁用大页内存导致的内存碎片
vm.compaction_proactiveness = 0

# 应用后生效
# sysctl -p

4.5 网卡 Ring Buffer

bash 复制代码
# 查看网卡 ring buffer 大小
ethtool -g eth0
# 关注: RX: 当前值和最大值

# 增大 ring buffer(如果支持)
ethtool -G eth0 rx 4096 tx 4096

# 查看是否因 ring buffer 溢出丢包
ethtool -S eth0 | grep -E "rx_dropped|rx_missed|rx_fifo|rx_over"
# 任何 > 0 的值都要警惕

五、应用层排查

5.1 EtherNet/IP 协议栈层面

复制代码
# 检查 EIP 应用日志
# 典型开源协议栈日志标志:
#   - OpENer: "connection timeout", "missed packet", "RPI violation"
#   - libplctag: "timeout", "connection lost"

# 关注以下指标:
#   1. Connection Timeout Multiplier 设置
#      - 连接超时 = RPI × Timeout Multiplier
#      - 默认 Multiplier=4,丢包超过这个倍数就断开连接
#   2. RPI 值是否合理(太小 → 网络压力大,太大 → 数据更新慢)
#      - 典型值:5ms ~ 100ms
#      - 若 RPI < 10ms,对网络质量要求极高

5.2 检查 RPI 与网络能力的匹配

bash 复制代码
# 假设 RPI = 5ms,每个 I/O 包 500 字节
# 每秒包数 = 1000 / 5 = 200 pps (单向)
# 若 10 个连接: 200 × 2(双向) × 10 = 4000 pps
# 检查网卡是否能承受此速率

# 实际测量 pps
tcpdump -i eth0 udp port 2222 -nn -c 10000 | pv -l > /dev/null
# 对比 pps 是否接近网卡/CPU处理极限

5.3 连接数量

bash 复制代码
# 查看当前 EIP 连接数
ss -u -a | grep 2222 | wc -l

# 过高的连接数 → 考虑拆分为多个 Scanner

六、交换机 / 网络设备排查

6.1 交换机侧

复制代码
# 工业交换机常见问题:
# 1. IGMP Snooping 未正确配置(使用多播时)
#    - 多播 I/O 包被交换机丢弃
#    - 解决:启用 IGMP Snooping 并正确配置 Querier

# 2. QoS 未配置
#    - I/O 包被大流量(如固件下载)挤占
#    - 解决:给 UDP 2222 端口配置高优先级(DSCP/CoS)

# 3. 交换机端口缓冲区溢出
#    - 微突发(microburst)导致瞬时丢包
#    - 解决:增大端口缓冲区或降低 RPI

# 4. 广播风暴
#    - 环路导致 → STP/RSTP/DLR 未开启或配置错误
#    - 解决:启用环网保护协议

6.2 网络拓扑

复制代码
# 检查拓扑:
# - EIP I/O 连接应尽可能在同 VLAN / 同子网
# - 跨 VLAN 路由会增加延迟和抖动
# - 链形/环形拓扑中任意节点故障会影响下游
# - 考虑使用 DLR(Device Level Ring)或 PRP(Parallel Redundancy Protocol)

七、排查流程总结

复制代码
                    发现 I/O 丢包
                         │
                         ▼
          ┌──────────────────────────┐
          │ 1. 确认丢包范围           │
          │  单个连接 or 全部连接?    │
          │  偶发 or 持续?           │
          └──────────┬───────────────┘
                     │
                     ▼
          ┌──────────────────────────┐
          │ 2. 物理层检查            │
          │  网线/光模块/双工匹配      │
          │  ethtool -S eth0         │
          │  有 CRC/物理错误 → 换线   │
          └──────────┬───────────────┘
                     │ 正常
                     ▼
          ┌──────────────────────────┐
          │ 3. 抓包分析 (tcpdump)     │
          │  udp port 2222           │
          │  Wireshark 查看间隔/序列号 │
          │  包确实丢了 → 下一步      │
          └──────────┬───────────────┘
                     │
                     ▼
          ┌──────────────────────────┐
          │ 4. 系统层面检查           │
          │  netstat -su 看 UDP 错误  │
          │  检查 ring buffer 溢出    │
          │  检查 socket Recv-Q      │
          │  有溢出 → 调大缓冲区      │
          └──────────┬───────────────┘
                     │ 正常
                     ▼
          ┌──────────────────────────┐
          │ 5. CPU/实时性检查         │
          │  中断分布/调度策略         │
          │  CPU 打满 → 调整亲和性    │
          │  非实时 → 改用 FIFO 调度  │
          └──────────┬───────────────┘
                     │ 正常
                     ▼
          ┌──────────────────────────┐
          │ 6. 交换机/网络检查         │
          │  IGMP Snooping / QoS     │
          │  端口统计 / 缓冲区溢出     │
          │  拓扑冗余/环网状态         │
          └──────────┬───────────────┘
                     │
                     ▼
          ┌──────────────────────────┐
          │ 7. 应用层检查             │
          │  RPI 是否合理             │
          │  Timeout Multiplier      │
          │  连接数量是否超限          │
          └──────────────────────────┘

八、快速诊断命令合集

bash 复制代码
#!/bin/bash
# eip_diag.sh --- EtherNet/IP I/O 丢包快速诊断

IFACE="eth0"
EIP_PORT=2222

echo "=== 1. 接口状态 ==="
ip link show $IFACE

echo ""
echo "=== 2. 接口错误统计 ==="
ip -s link show $IFACE

echo ""
echo "=== 3. 网卡硬件统计 ==="
ethtool -S $IFACE | grep -iE "drop|error|miss|fifo|over|crc"

echo ""
echo "=== 4. Ring Buffer 大小 ==="
ethtool -g $IFACE

echo ""
echo "=== 5. UDP 统计 ==="
netstat -su | grep -A 10 Udp:

echo ""
echo "=== 6. Socket 接收队列 ==="
ss -u -a | grep $EIP_PORT

echo ""
echo "=== 7. 内核丢包计数 ==="
cat /proc/net/dev | awk -v iface="$IFACE:" '$1==iface {print "RX drop:",$5,"TX drop:",$9}'

echo ""
echo "=== 8. 中断分布 ==="
grep $IFACE /proc/interrupts | head -5

echo ""
echo "=== 9. CPU 负载 ==="
uptime

echo ""
echo "=== 10. 当前 UDP 缓冲区配置 ==="
sysctl net.core.rmem_max net.core.rmem_default net.core.wmem_max net.core.wmem_default
相关推荐
新时代牛马1 小时前
Linux 信号处理完整篇:从sigaction、掩码到内核发送与可重入排障
linux·运维·信号处理
程序员梅雨1 小时前
Linux & Shell 实用干货
linux·运维·服务器·后端·面试·php
吴声子夜歌1 小时前
编写Shell脚本——自顶向下设计
linux·运维·shell
新晨单片机设计1 小时前
S010A-基于STM32单片机水温控制系统(数码管显示)【Proteus仿真+Keil程序+原理图】
stm32·单片机·嵌入式硬件
M78佐菲2 小时前
HTML学习笔记
linux·笔记·学习·tcp/ip·html
新时代牛马2 小时前
嵌入式 Linux 蓝牙框架完整篇:从 HCI、L2CAP 到BlueZ 用户态
linux·运维·服务器
事界见闻2 小时前
高性能电机控制MCU选型对比:精度、功耗与实时性如何兼得?
单片机·嵌入式硬件
Brilliantwxx2 小时前
【STM32】 初认识USART串口
linux·stm32·单片机·嵌入式硬件·架构
aningx2 小时前
使用自定义bash脚本预防oom卡死
linux