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 初始化时按 Ctrl+B 或某个特定键(各家厂商不同),进入 PXE 固件的配置菜单。那里能看到 MAC 地址、DHCP 状态、甚至手动指定 TFTP 服务器。

我曾经遇到过一块网卡,PXE ROM 版本太老,不支持 UEFI 的 HTTP Boot,折腾了两天才发现是固件问题。所以,排查 PXE 故障时,先确认网卡 ROM 版本是个好习惯。

2.2 DHCP 与 ProxyDHCP 的交互流程

标准的 PXE 启动,DHCP 不只是分配 IP,还要告诉客户端「启动文件在哪」。但现实网络里,DHCP 服务器往往由网络管理员统一管理,不能随便改配置。这时候 ProxyDHCP 就派上用场了。

整个交互流程是这样的:

  1. 客户端广播 DHCPDISCOVER
  2. 普通 DHCP 服务器回复 DHCPOFFER,包含 IP 地址
  3. ProxyDHCP 服务器也回复 DHCPOFFER,但不包含 IP 地址,只带 Option 60(客户端标识)、Option 66(TFTP 服务器)、Option 67(启动文件名)
  4. 客户端根据收到的信息,决定从哪个服务器下载启动文件

关键区别:普通 DHCP 给 IP,ProxyDHCP 给启动参数。两者可以跑在同一台机器上,也可以分开部署。我建议在生产环境中把 DHCP 和 ProxyDHCP 分离,避免改 DHCP 配置影响其他业务。

为什么需要 ProxyDHCP?你想想看,如果网络里已经有现成的 DHCP 服务器,你为了部署 PXE 去改它的配置,风险不小。ProxyDHCP 就像个「旁路插件」,只关心 PXE 相关的选项,不影响原有 IP 分配逻辑。

避坑指南:我曾经遇到一个案例,ProxyDHCP 和 DHCP 服务器回复的 Option 66 不一致,客户端随机选了一个,导致启动文件下载失败。解决办法是让 ProxyDHCP 的 Option 60 值("PXEClient")比普通 DHCP 更具体,客户端会优先采用 ProxyDHCP 的启动参数。

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
传输效率 低(高延迟下尤其明显) 高(一次发送,多机接收)
实际经验:我在做学校机房批量部署时,50 台机器同时 PXE 启动,用 TFTP 的话网络直接被打满,而且每台机器都要单独传一遍内核。换成 MTFTP 后,一次多播搞定,部署时间从 40 分钟降到 5 分钟。但要注意,老旧交换机可能不支持 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 启动更像是一个「操作系统加载操作系统」的过程。

我曾经踩过的坑:在 UEFI 环境下部署 PXE,忘记关闭 Secure Boot,结果 bootx64.efi 没签名,启动直接报错「Security Violation」。解决办法要么给 EFI 文件签名,要么在 BIOS 设置里关掉 Secure Boot。另外,UEFI 的 HTTP Boot 虽然快,但需要 DHCP 服务器支持 Option 60 和 61,很多旧设备不兼容。

还有一个细节:BIOS 下 PXE 启动时,网卡 ROM 会接管中断向量,占用 0x60-0x66 等低端内存。UEFI 下则没有这个问题,因为 UEFI 运行在保护模式或长模式下,内存管理更规范。

2.5 整体交互流程(SVG 图)

下面这张图展示了 PXE 启动的完整交互流程,从客户端上电到加载启动文件:

PXE 启动完整交互流程 PXE 启动完整交互流程 PXE 客户端 DHCP 服务器 ProxyDHCP TFTP 服务器 ① DHCPDISCOVER(广播) ② DHCPOFFER(IP + 选项) ③ 再次广播(带 PXE 标识) ④ ProxyDHCP OFFER(启动参数) ⑤ TFTP RRQ(启动文件) ⑥ TFTP DATA + ACK(循环) 关键说明 • 步骤①-②:获取 IP 地址 • 步骤③-④:获取启动参数 • 步骤⑤-⑥:下载启动文件 • ProxyDHCP 可省略(合设时)

从图上可以清楚看到,PXE 启动分三个阶段:IP 获取、参数获取、文件下载。每个阶段都有对应的协议和服务器角色。实际部署时,DHCP 和 ProxyDHCP 可以合并,但 TFTP 服务器通常是独立的——除非你用的是 dnsmasq 这种全能型工具。

总结一下:PXE 协议栈的核心就是「固件初始化 → DHCP/ProxyDHCP 协商 → TFTP 下载 → 执行启动文件」。BIOS 和 UEFI 的差异主要体现在启动文件格式、协议栈能力和安全机制上。理解这些细节,你就能在遇到 PXE 启动失败时,快速定位是 DHCP 的问题、TFTP 的问题,还是固件兼容性的问题。