定制板多网口怎么做?我从 Switch、NIC、PHY 到 Linux 的一次实战梳理
📺B站 嵌入式孙老师:博主个人介绍
📘博主书籍-京东购买链接:Yocto项目实战教程
📘加博主微信,进技术交流群:jerrydev
最近在梳理一套多网口 Ethernet 设计时,发现真正难的并不是“多接几个 RJ45”,而是很容易把NIC、MAC、PHY、Switch、MDI、SGMII,以及 Linux 里的 ethX混在一起。
刚开始看原理图时,我也有过几个很自然的疑问:
- 一颗 Switch 扩出 4 个网口,Linux 会不会多出 4 个
ethX? - Switch 会不会给下面的设备分配 IP?
- 网卡已经能在
ifconfig里看到,为什么还是Link detected: no? - PHY 的 MDI 到底是数字信号还是物理信号?
- 两颗 PHY 都有 MDI,是不是直接连起来就行?
- 硬件 Link 通了之后,DHCP 又应该由谁负责?
把这些问题逐个拆开以后,我对多网口设计的理解反而变得比较简单了:
Linux / Application │ TCP/IP │ NIC / MAC │ Uplink │ Switch ┌────┼────┐ │ │ │ PHY PHY PHY │ │ │ MDI MDI MDI │ │ │ Magnetics │ RJ45这篇就沿着这条链,把我这次学习和排查过程中最容易混淆的地方整理一下。
1. 多网口设计,先把硬件角色分清楚
NIC:CPU 接入 Ethernet 的入口
NIC 全称Network Interface Controller,工程里直接理解成网卡就可以。
常见有两种:
SoC → PCIe → Ethernet NIC或者:
SoC → USB3 → Ethernet NIC这类设备被 Linux 驱动以后,通常会变成:
eth0 eth1 eth2很多 PCIe / USB Ethernet NIC 内部其实已经包含:
NIC ┌───────────────┐ │ PCIe / USB │ │ ↓ │ │ MAC │ │ ↓ │ │ PHY │ └───────────────┘ ↓ MDI所以NIC 和 PHY 不是一回事。
NIC 是一套完整的网络接口设备,PHY 只是其中负责 Physical Layer 的一部分。
MAC:处理 Ethernet Frame
MAC 还是数字世界里的东西。
Application ↓ TCP / UDP ↓ IP ↓ Ethernet Frame ↓ MACMAC 主要处理:
MAC Address Ethernet Frame CRC TX / RX Queue Flow Control它知道怎么收发 Ethernet Frame,但还没有真正开始驱动网线。
PHY:从数字 Ethernet 进入真正的物理层
PHY 才是真正的 Physical Layer Transceiver:
MAC │ │ Digital Ethernet ▼ PHY │ │ Physical Signal ▼ 传输介质PHY 内部会完成:
Auto-Negotiation 编码 / 解码 调制 / 解调 均衡 时钟恢复 回波消除 模拟前端所以后来我觉得,把 PHY 简单理解成“电平转换芯片”是不准确的。
它实际上实现了一整套 Ethernet Physical Layer。
MDI:PHY 最靠近网线的一侧
这是我这次比较容易混淆的一个概念。
MDI:
Medium Dependent Interface
1G / 2.5GBASE-T 常见四对:
MDI_A_P / MDI_A_N MDI_B_P / MDI_B_N MDI_C_P / MDI_C_N MDI_D_P / MDI_D_N这里已经不是普通的 0/1 数字接口,而是Copper Ethernet PHY 的模拟差分物理信号。
所以标准铜口结构应该这样看:
MAC ↓ PHY ↓ MDI ↓ Magnetics ↓ RJ45 ↓ Twisted Pair CableMagnetics,也就是 Ethernet 变压器,主要处理隔离、共模、EMC 等问题。
它并不是负责“数字变模拟”的——这个工作在 PHY 里面已经完成了。
我后来再看到MDIP/MDIN、MDI_A/B/C/D这类引脚时,就会直接想到:
这里已经进入 Copper Physical Layer 了。
Switch:不是网卡,而是多 Port 二层交换
这是另一个一开始很容易混的地方。
NIC 做的是:
CPU → NetworkSwitch 做的是:
CPU/Uplink │ ┌─ Switch ─┐ │ │ │ Port0 Port1 Port2 ...Switch 会学习:
MAC_A → Port0 MAC_B → Port2 MAC_C → Uplink以后收到 Ethernet Frame,就根据目的 MAC 地址决定从哪个 Port 发出去。
所以我现在最简单的区分就是:
NIC 负责 CPU 怎么进入 Ethernet;Switch 负责多个 Ethernet Port 之间怎么交换 Frame。
这也是为什么一颗多口 Switch 本身不能简单当成“多口网卡”。
4 个 RJ45,不代表 Linux 一定有 4 个 ethX
这个问题我一开始也专门确认过。
假设:
Linux │ eth1 │ Switch ├─ Port0 → RJ45 ├─ Port1 → RJ45 ├─ Port2 → RJ45 └─ Port3 → RJ45Linux 完全可能只看到:
eth1下面 4 个接口只是 Switch 内部的 Port。
因为:
eth1 = CPU 自己的网络接口 Port0~3 = Switch 的交换端口两者不是一个概念。
如果使用 Linux DSA(Distributed Switch Architecture),情况会不同,Switch 的 User Port 可以表现成:
swp0 swp1 swp2 swp3但那需要具体 Switch 有对应的 Kernel Driver 和软件架构支持。
所以更准确地说:
多个 RJ45 ≠ 多个 Linux NIC这条结论对我理解 Switch 很有帮助。相关学习记录里也正是通过“Switch 自主工作”和 Linux Uplink 两个层面把这件事拆开的。
RGMII、SGMII 和 MDI 不要混在一起
在 Ethernet 原理图里,它们经常同时出现。
但其实非常好区分。
RGMII:
MAC │ RGMII │ PHY / Switch属于并行数字接口。
SGMII:
MAC / Switch │ SGMII │ PHY属于高速串行数字接口。
MDI:
PHY │ MDI │ Magnetics │ Cable属于铜口物理层接口。
所以我最后直接记成:
| 接口 | 怎么理解 |
|---|---|
| RGMII | 芯片间并行数字 Ethernet 接口 |
| SGMII | 芯片间串行数字 Ethernet 接口 |
| MDI | Copper PHY 面向介质的物理接口 |
另外,SerDes 也不等于 SGMII。
SerDes 是 Serializer / Deserializer,是高速串行收发能力;SGMII、2500BASE-X、USXGMII 等才是具体的 Ethernet 接口形式。
Switch 的 Port 也分类型
看到:
6-Port Switch不能马上理解成:
6 × RJ45真正应该看的是:
几个 Port 自带 Copper PHY? 几个是 SerDes Port? 哪个是 CPU/Uplink Port? 支持什么速率?如果 Switch Port 自带 PHY:
Switch │ Integrated PHY │ MDI │ Magnetics │ RJ45这类最适合直接做普通铜口。
如果 Port 只是 SerDes:
Switch │ SGMII / 2500BASE-X │ External PHY │ MDI │ Magnetics │ RJ45就还需要一颗外部 PHY。
我这次也是在分析一条Switch SerDes → External PHY → MDI的链路后,才真正把这两个 Port 类型区分清楚。
板内如果能走 SerDes,就尽量不要反复进出 Copper PHY
从架构上看,我现在更喜欢:
SoC MAC │ SGMII / USXGMII │ Switch真正到了板外才:
Switch ↓ PHY ↓ MDI ↓ Magnetics ↓ RJ45而如果出现:
NIC ↓ Copper PHY ↓ MDI ↓ Copper PHY ↓ SerDes ↓ Switch链路就会复杂很多。
这里我实际碰到过一个很有价值的问题:
两颗 PHY 都有 MDI,能不能直接把两组 MDI 接在一起?
答案不能简单理解成“接口名字一样就能接”。
MDI 是模拟 Physical Layer 接口,标准铜口是按照 PHY 厂商的 Reference Design、Magnetics 和 Cable Channel 去设计的。
如果要做 PHY-to-PHY 的 transformerless / back-to-back 连接,必须确认两颗 PHY 是否明确支持,以及偏置、阻抗、耦合等要求。
不能把它当成:
TXP/N 直接接 RXP/N这种普通数字 SerDes 来处理。
这也是我这次排查中最有价值的一个硬件认识:**MDI 和 SGMII 虽然都是差分线,但根本不是一类信号。**相关实测笔记里,PHY 的 SerDes 一侧和 MDI 一侧也表现出了完全不同的连接方式。
Switch 本身也有启动过程
另一个之前容易忽略的地方是:
Switch 并不是“上电就天然开始交换数据”。
一颗复杂一点的 Switch 通常还有:
Power ↓ Clock ↓ Reset ↓ Strap Sampling ↓ SPI Flash / EEPROM ↓ Firmware / Configuration ↓ PHY / SerDes Init ↓ Port Forwarding所以看 Switch 电路时,除了数据线,我现在还会先找:
Power Clock Reset Strap SPI Flash / EEPROM MDIO / SPI / I2CStrap 可能决定:
Boot Mode PHY Enable Port Mode Clock Mode Management Interface因此一个 Switch 没工作,不一定和 Linux 有关系。
有时候 Linux 根本就没有参与它的启动。实际学习记录里也能看到典型的 Strap、SPI Flash、自启动和 PHY Port 组合。
2. 软件和调试:我后来不再一上来就 ping
ethX出现,只能证明网卡这一层起来了
比如:
iplink能够看到:
eth1如果是 USB NIC:
lsusb也正常。
如果是 PCIe NIC:
lspci也正常。
这些只能证明:
SoC ↓ PCIe / USB ↓ NIC ↓ Driver ↓ ethX这部分工作了。
它并不能证明:
PHY ↓ Cable ↓ Switch已经建立 Link。
这个区别是我这次排查过程中很重要的一步。
UP和Link detected: yes不是一回事
例如:
iplinkshow eth1看到:
<NO-CARRIER,BROADCAST,MULTICAST,UP>这里的:
UP只表示:
软件把这个接口打开了。
真正判断 Physical Link,我更关注:
ethtooleth1里面:
Link detected: yes以及:
cat/sys/class/net/eth1/carrier正常应该:
1再看:
LOWER_UP RUNNING这些状态。
我实际遇到过:
UP 但是 NO-CARRIER Link detected: no carrier = 0这时候 IP 配置得再漂亮也没有意义。
因为问题还停留在 Physical Layer。这个区分在实际日志分析里非常明显。
Link 正常以后,第二步应该看 Frame,而不是马上 ping
Physical Link 成立以后:
ip-slinkshow eth1看:
RX packets TX packets有没有增长。
然后:
tcpdump-ieth1-e-n看有没有:
ARP DHCP IPv6 Broadcast Multicast这一步很重要,因为:
Ethernet Frame 能不能收到,和 IP 是否在同一个网段是两件事情。
即使 IP 没配对,只要对端有广播、ARP 或 DHCP:
RX packets照样可以增长。
因此我现在排网口基本按:
有没有 Carrier? ↓ 有没有 Ethernet Frame? ↓ 有没有 IP?来走。
169.254.x.x不是 Switch 分配的 IP
这也是我学习过程中一个比较典型的误区。
看到一个下游设备拿到了:
169.254.x.x第一反应很容易是:
是不是 Switch 给它分配了地址?
其实一般不是。
169.254.0.0/16是 IPv4 Link-Local。
很多系统在:
DHCP 请求 ↓ 没有服务器回应 ↓ 自己选择一个 169.254.x.x作为兜底地址。
所以出现:
169.254.x.x反而经常是在告诉我们:
DHCP 没成功。
对于普通二层 Switch 来说,它负责的是 Ethernet Frame 转发,而 DHCP Server 通常运行在主机、路由器或专门的网络服务上。
DHCP Server 应该放在哪里?
假设:
Linux 主机 │ eth1 │ Switch ├─ Device A ├─ Device B └─ Device C如果希望下面的设备自动获得:
192.168.100.x可以在 Linux 主机上跑:
dnsmasq过程其实很简单:
Device │ │ DHCPDISCOVER ▼ Switch │ ▼ eth1 │ ▼ dnsmasq │ │ DHCPOFFER / DHCPACK ▼ Device 得到 IPSwitch 在这里依然只是转发二层广播。
它并不负责:
“给谁分什么 IP”一个 dnsmasq 可以服务多个接口,但网段要分开
我这次还碰到了一个纯软件问题:
一台设备上可能同时有:
Wi-Fi AP Ethernet Port A Ethernet Port B都需要 DHCP。
没有必要启动三个 dnsmasq,一个实例就可以:
bind-interfaces interface=wlan-ap dhcp-range=set:wifi,192.168.10.10,192.168.10.100,12h dhcp-option=tag:wifi,3,192.168.10.1 dhcp-option=tag:wifi,6,192.168.10.1 interface=eth1 dhcp-range=set:lan1,192.168.20.10,192.168.20.100,12h dhcp-option=tag:lan1,3,192.168.20.1 dhcp-option=tag:lan1,6,192.168.20.1这里后来我才注意到一个细节:
dhcp-option=3 dhcp-option=6如果不加 tag,可能会错误地应用到其他接口。
所以多网段时最好配成:
set:xxx tag:xxx让 Gateway 和 DNS 跟对应地址池绑定。
这部分也是我在实际增加多个 DHCP 网段时踩到的一个软件配置点。
systemd 依赖也可能让“DHCP 配置正确但服务就是起不来”
还有一个问题跟 Ethernet 本身几乎没有关系,却很容易误判成网络问题。
例如:
dnsmasq.service被 systemd 强绑定到某一个网卡:
BindsTo=sys-subsystem-net-devices-xxx.device当这个接口不存在时:
Dependency failed整个 dnsmasq 都起不来。
如果 dnsmasq 同时服务多个独立接口,这种强依赖反而可能不合理:
接口 A 不存在 ↓ dnsmasq 整体启动失败 ↓ 接口 B 的 DHCP 也一起没了后来我更倾向把:
数据接口是否存在和:
DHCP Service 是否允许启动分开考虑。
这种问题如果只盯着dnsmasq.conf本身,很容易找错方向。
最后形成了一套比较固定的 Ethernet 排查顺序
现在再遇到多网口不通,我基本不会从ping开始。
我会按:
1. Power ↓ 2. Clock / Reset / Strap ↓ 3. NIC 是否枚举 ↓ 4. Switch 是否 Boot ↓ 5. PHY / SerDes Link ↓ 6. Carrier ↓ 7. Ethernet Frame ↓ 8. DHCP / ARP ↓ 9. IP / Route ↓ 10. Application具体 Linux 命令也很固定:
# 网卡有没有iplink# 物理 Linkethtooleth1cat/sys/class/net/eth1/carrier# 二层有没有包ip-slinkshow eth1 tcpdump-ieth1-e-n# DHCPjournalctl-udnsmasq-f# 最后才是ipaddriprouteping这样最大的好处,是能够快速判断问题到底属于:
硬件 PHY Switch Driver DHCP 还是 IP而不是统称为“网口不通”。
3. 这次学习下来,我觉得最值得记住的几件事
第一次接触多网口 Switch 设计时,很容易从 RJ45 数量出发:
有 4 个网口 → 应该有 4 个网卡 → 应该有 4 个 IP后来发现这个思路本身就不对。
更合理的理解应该是:
CPU │ NIC / MAC │ Uplink │ Switch ├─ Port ├─ Port ├─ Port └─ PortSwitch Port、Linux NIC 和 IP Address 本来就是三个不同层面的东西。
另外一个比较深的体会,是 Ethernet 原理图不能只看“差分线有没有接上”。
下面这些:
RGMII SGMII USXGMII MDI虽然都在传 Ethernet,但处在完全不同的位置。
特别是:
SGMII → 芯片间数字接口 MDI → Copper PHY 模拟物理接口如果这一层没分清,后面很容易把两种完全不同的差分信号当成同一种东西。
最后则是软件。
多网口不是把硬件 Link 做出来就结束了,还要继续考虑:
Switch 谁初始化? PHY 谁管理? Linux 能看到哪些接口? DHCP Server 放在哪里? 多个网段怎么隔离? 服务启动顺序是否合理?所以现在如果让我再画一次多网口架构,我不会先画 RJ45,而是先画:
Linux / Application │ TCP/IP │ NIC / MAC │ Uplink 带宽 │ Switch ┌─────────┼─────────┐ │ │ │ PHY PHY PHY │ │ │ MDI MDI MDI │ │ │ Magnetics Magnetics Magnetics │ │ │ RJ45 RJ45 RJ45然后逐层问:
SoC 怎么接入 Switch? Uplink 带宽够不够? Switch Port 是内置 PHY, 还是 SerDes? MDI 后面的电气设计是否正确? Switch 是自主启动, 还是由 BSP / Linux 配置? Linux 应该看到 ethX, 还是 DSA 的 swpX? DHCP 又由谁负责?这些问题回答清楚之后,多网口设计就不会再是一堆 NIC、PHY 和 Switch 芯片堆在一起。
对我来说,这次最大的收获也不是记住了某颗芯片,而是终于能把“网络数据从 Linux 一直走到网线,中间到底经过了什么”顺下来。
后面无论换 1G、2.5G、5G 还是 10G,器件和接口会变,但这套分析方法基本不会变。
