PXE 协议栈详解:从固件到传输的完整链路
PXE 启动,说白了就是让一台裸机通过网络把操作系统拉起来。很多刚入行的朋友以为它就是个 DHCP 加 TFTP,其实里面的门道不少。我最早接触 PXE 是在一个数据中心项目里,几百台机器要批量部署,结果一半卡在 PXE 超时上——嗯,那时候我才开始认真啃协议栈的细节。
这一章我们拆开来看:固件怎么跟网卡 ROM 配合,DHCP 和 ProxyDHCP 到底谁说了算,TFTP 为什么慢但还得用它,以及 BIOS 和 UEFI 下完全不同的启动路径。
2.1 PXE 固件与网卡 ROM
每块支持 PXE 的网卡,ROM 里都烧了一段固件代码。机器上电后,BIOS 或 UEFI 会调用这段代码,它负责初始化网络接口、发送 DHCP 请求、下载启动文件。
我个人习惯把 PXE 固件分成三层:
- 底层驱动:控制网卡收发数据包,实现简单的 UDP/IP 栈
- 中间层协议:处理 DHCP、ARP、TFTP 这些协议
- 上层 API:提供给 BIOS/UEFI 调用的接口,比如下载文件、获取配置
我曾经遇到过一块网卡,PXE ROM 版本太老,不支持 UEFI 的 HTTP Boot,折腾了两天才发现是固件问题。所以,排查 PXE 故障时,先确认网卡 ROM 版本是个好习惯。
2.2 DHCP 与 ProxyDHCP 的交互流程
标准的 PXE 启动,DHCP 不只是分配 IP,还要告诉客户端「启动文件在哪」。但现实网络里,DHCP 服务器往往由网络管理员统一管理,不能随便改配置。这时候 ProxyDHCP 就派上用场了。
整个交互流程是这样的:
- 客户端广播 DHCPDISCOVER
- 普通 DHCP 服务器回复 DHCPOFFER,包含 IP 地址
- ProxyDHCP 服务器也回复 DHCPOFFER,但不包含 IP 地址,只带 Option 60(客户端标识)、Option 66(TFTP 服务器)、Option 67(启动文件名)
- 客户端根据收到的信息,决定从哪个服务器下载启动文件
关键区别:普通 DHCP 给 IP,ProxyDHCP 给启动参数。两者可以跑在同一台机器上,也可以分开部署。我建议在生产环境中把 DHCP 和 ProxyDHCP 分离,避免改 DHCP 配置影响其他业务。
为什么需要 ProxyDHCP?你想想看,如果网络里已经有现成的 DHCP 服务器,你为了部署 PXE 去改它的配置,风险不小。ProxyDHCP 就像个「旁路插件」,只关心 PXE 相关的选项,不影响原有 IP 分配逻辑。
2.3 TFTP 与 MTFTP 的传输机制
TFTP 是个老协议,基于 UDP,端口 69。它简单到只有五种报文:RRQ、WRQ、DATA、ACK、ERROR。没有认证,没有加密,传输效率也低——但 PXE 固件里只集成了它,你没得选。
TFTP 的传输过程:
- 客户端发 RRQ(读请求),指定文件名和传输模式(octet 或 netascii)
- 服务器回复 DATA 块,每块 512 字节(最后一块可以小于 512)
- 客户端每收到一块,回复 ACK
- 典型的「停等协议」——发一块,等确认,再发下一块
这就导致一个问题:高延迟网络下,TFTP 慢得让人抓狂。比如跨机房部署,RTT 50ms,传一个 10MB 的内核文件,理论上要 512 字节 × 20000 块 × 50ms = 1000 秒,16 分钟!
MTFTP(多播 TFTP)就是为了解决这个问题的。它允许一台服务器同时向多台客户端发送数据,用多播 IP 地址(通常是 224.0.0.1 或指定范围)。客户端加入多播组,接收数据块,不需要每个客户端单独确认。
| 特性 | TFTP | MTFTP |
|---|---|---|
| 传输方式 | 单播,点对点 | 多播,一对多 |
| 确认机制 | 每块都需要 ACK | 基于 NACK 或窗口确认 |
| 适用场景 | 单机部署、小文件 | 批量部署、大文件 |
| 网络要求 | 任意网络 | 交换机需支持 IGMP Snooping |
| 传输效率 | 低(高延迟下尤其明显) | 高(一次发送,多机接收) |
2.4 BIOS/UEFI 下的 PXE 启动差异
BIOS 和 UEFI 下的 PXE 启动,表面上看都是「网卡 ROM → DHCP → TFTP → 加载文件」,但底层差异很大。我整理了一张对比表:
| 对比项 | BIOS (Legacy) | UEFI |
|---|---|---|
| 启动文件格式 | pxelinux.0(二进制) | bootx64.efi(PE32+ 格式) |
| 网络协议栈 | PXE ROM 自带的 UDP/IP 栈 | UEFI 网络协议栈(更完整,支持 IPv6) |
| 文件系统支持 | 无,直接加载到内存执行 | 支持 FAT、ISO9660,可读取文件系统 |
| 安全启动 | 不支持 | 支持 Secure Boot,需要签名 |
| HTTP Boot | 不支持 | UEFI 2.5+ 支持,可用 HTTP 替代 TFTP |
| 启动文件路径 | 通常直接放在 TFTP 根目录 | 需遵循 EFI 规范路径,如 \EFI\BOOT\BOOTX64.EFI |
BIOS 下的 PXE 启动,固件加载 pxelinux.0 后,它本身就是一个微型操作系统,负责解析配置文件、加载内核和 initrd。整个过程完全在实模式下运行,内存限制大,功能也有限。
UEFI 下的 PXE 启动则不同。UEFI 固件本身就是一个完整的运行时环境,它加载 bootx64.efi 后,这个 EFI 应用可以调用 UEFI 提供的各种服务——网络、文件系统、图形输出等。说白了,UEFI 下的 PXE 启动更像是一个「操作系统加载操作系统」的过程。
还有一个细节:BIOS 下 PXE 启动时,网卡 ROM 会接管中断向量,占用 0x60-0x66 等低端内存。UEFI 下则没有这个问题,因为 UEFI 运行在保护模式或长模式下,内存管理更规范。
2.5 整体交互流程(SVG 图)
下面这张图展示了 PXE 启动的完整交互流程,从客户端上电到加载启动文件:
从图上可以清楚看到,PXE 启动分三个阶段:IP 获取、参数获取、文件下载。每个阶段都有对应的协议和服务器角色。实际部署时,DHCP 和 ProxyDHCP 可以合并,但 TFTP 服务器通常是独立的——除非你用的是 dnsmasq 这种全能型工具。
总结一下:PXE 协议栈的核心就是「固件初始化 → DHCP/ProxyDHCP 协商 → TFTP 下载 → 执行启动文件」。BIOS 和 UEFI 的差异主要体现在启动文件格式、协议栈能力和安全机制上。理解这些细节,你就能在遇到 PXE 启动失败时,快速定位是 DHCP 的问题、TFTP 的问题,还是固件兼容性的问题。