1. PXE网络启动基础:协议栈架构与交互流程

PXE这东西,说白了就是让一台裸机通过网络把操作系统拉起来。我最早接触它是在做无盘工作站项目的时候,那时候机房几十台机器都没硬盘,全靠PXE启动。嗯,今天咱们就把它的底裤扒干净。

1.1 PXE协议栈长什么样?

PXE不是单一协议,它是几个协议的组合拳。我习惯把它分成三层来看:

  • 底层:UDP/IP — 无连接传输,快但不靠谱
  • 中间层:DHCP — 拿IP地址和启动参数
  • 应用层:TFTP — 下载启动文件

你想想看,一台刚通电的机器,没有操作系统,没有IP地址,怎么跟网络通信?PXE固件(写在网卡的Option ROM里)硬编码了一套最小化的UDP协议栈。它不需要完整的TCP/IP,因为TFTP和DHCP都跑在UDP上。

关键点:PXE客户端只支持UDP,不支持TCP。这意味着所有传输都得靠UDP的简单机制,丢包重传由TFTP自己搞定。

我在项目中遇到过一个问题:某些UEFI网卡默认关闭了PXE Option ROM,导致机器死活起不来。后来进BIOS把"Network Stack"打开才解决。嗯,这坑我踩过。

1.2 DHCP交互流程 — 不只是拿IP

PXE的DHCP跟普通DHCP不太一样。普通DHCP只给你IP地址、网关、DNS。PXE的DHCP还要告诉你:TFTP服务器在哪、启动文件名是什么。

具体流程分四个阶段:

  1. DHCP Discover(广播) — 客户端喊:"谁给我个IP?顺便告诉我启动服务器在哪!"
  2. DHCP Offer(单播/广播) — 服务器回应,里面带Option 54(Server Identifier)和Option 60(Class Identifier,标记这是PXE客户端)
  3. DHCP Request(广播) — 客户端说:"就你了,给我配置吧。"
  4. DHCP Ack(单播) — 服务器确认,同时塞入Option 66(TFTP服务器地址)和Option 67(启动文件名)

个人习惯:我一般会在DHCP服务器上把Option 66和67写死,而不是依赖Proxy DHCP。因为Proxy DHCP多一次交互,容易出兼容性问题。

这里有个容易翻车的地方:如果DHCP服务器返回的Option 67是相对路径(比如"boot/pxelinux.0"),PXE客户端会把它拼接到TFTP根目录后面。我曾经因为路径写错,折腾了一下午。

1.3 TFTP传输 — 慢但可靠

TFTP是PXE的传输主力。它没有认证,没有加密,纯靠UDP加ACK确认。每次传输固定512字节的数据块,收到ACK才发下一块。

TFTP的传输模式:

模式 说明 典型用途
octet 二进制模式,逐字节传输 内核、initrd、引导加载器
netascii 文本模式,自动转换换行符 配置文件(极少用)

我建议你只用octet模式。netascii模式在跨平台时会把换行符搞乱,我吃过这个亏。

警告:TFTP默认端口是69,但数据端口是动态协商的。防火墙如果只开放69端口,数据包会被拦截。我建议开放UDP 1024-65535,或者用tftp-hpa的--port-range参数限制端口范围。

1.4 启动文件加载顺序

PXE客户端拿到TFTP服务器地址和启动文件名后,就开始下载文件了。但具体下载哪个文件,取决于固件类型和配置。

典型的加载顺序(以BIOS/Legacy模式为例):

  1. pxelinux.0 — 第一阶段引导加载器,由syslinux提供
  2. ldlinux.c32 — pxelinux.0的库文件,必须跟pxelinux.0在同一目录
  3. pxelinux.cfg/default — 配置文件,决定启动哪个内核
  4. vmlinuz — Linux内核
  5. initrd.img — 初始根文件系统

如果是UEFI模式,文件名会变成:

  • bootx64.efi(x86_64架构)
  • bootia32.efi(IA32架构)
  • 或者自定义的grubx64.efi

为什么会这样?因为UEFI固件只认EFI可执行文件,不认pxelinux.0这种DOS时代的引导器。

下面这张图把整个流程串起来了:

PXE网络启动完整流程 PXE网络启动完整流程 PXE客户端(裸机) DHCP服务器 TFTP服务器 ① DHCP Discover ② DHCP Offer(含Option 66/67) ③ DHCP Request ④ DHCP Ack(确认配置) ⑤ TFTP RRQ(请求启动文件) ⑥ TFTP DATA(512字节/块) 启动文件加载顺序(BIOS/Legacy模式) 1. pxelinux.0 — 第一阶段引导器,由TFTP下载到内存执行 2. ldlinux.c32 — pxelinux.0的库文件,自动加载 3. pxelinux.cfg/default — 配置文件,决定内核和参数 4. vmlinuz — Linux内核镜像 5. initrd.img — 初始根文件系统 ⚠ UEFI模式:bootx64.efi → grub.cfg → 内核 → initrd

1.5 实际项目中的避坑指南

讲几个我踩过的坑,你遇到了能少走弯路:

坑1:TFTP传输超时

我曾经在跨网段部署PXE时,TFTP传输到一半就断了。原因是TFTP的UDP包被三层交换机拦截了。解决方案:在DHCP的Option 66里填TFTP服务器的实际IP,不要填网关IP。另外,tftp-hpa的--timeout参数可以调大重试间隔。

坑2:启动文件名大小写

TFTP服务器(特别是tftpd-hpa)默认区分大小写。我遇到过把"pxelinux.0"写成"Pxelinux.0"导致404的情况。建议统一用小写,并在DHCP配置里写绝对路径。

调试技巧:在DHCP服务器上开启日志(journalctl -u isc-dhcp-server -f),可以看到客户端请求的Option 60和67。如果客户端没发Option 60,说明PXE固件没启用。这时候去BIOS里找"Network Boot"或"UEFI Network Stack"。

1.6 小结

PXE启动的核心就三件事:拿IP、拿文件名、拿文件。DHCP负责前两件,TFTP负责最后一件。启动文件的加载顺序是链式的:先下载引导器,引导器再下载配置文件,配置文件告诉它下载哪个内核和initrd。