无盘 Linux X 终端
X 终端并非新概念;NCD 等公司生产 X 终端已超过 15 年。然而,在 1990 年代后期,瘦客户端的概念逐渐过时,因为 PC 硬件的价格降得如此之低,以至于使用 X 终端不再具有明显的成本优势。关于瘦客户端与 PC 的总体拥有成本(包括硬件成本和管理支持)的激烈争论随之而来,而本文不会解决这场辩论。本文的目的仅仅是描述一种技术,该技术允许人们利用不断增长的废旧硬件(在 PC 技术进步的浪潮中遗留下来的)来构建 X 终端。
任何瘦客户端的基本特征是它应该几乎没有或没有持久存储。通常,专用 X 终端具有少量 NVRAM,用于存储配置选项,除此之外别无其他。实际上,通常甚至可以将这些选项放在存储在服务器上的配置文件中,并在终端启动时由终端下载。本文采用纯粹主义的观点,即 X 终端应该没有任何持久存储。
PC 没有硬盘、软盘或 CD-ROM 驱动器,因此必须由其他设备提供引导加载程序和可引导映像。X 终端是其所处网络的产物,因此显而易见的选择是网络接口卡 (NIC)。因此,NIC 必须向 BIOS 标识自身为可引导设备。如果被选中,它必须能够从网络下载引导加载程序。这不是大多数普通 NIC 可以做到的。但是,Intel 发布了一个名为 PXE(预启动执行环境,发音为 pixie)的 NIC 引导 ROM 标准,并且该公司以及其他一些供应商在某些产品中也实现了该标准。许多带有内置以太网的新型主板都支持 PXE。
在准备本文时,我测试了五种不同的 NIC,所有这些 NIC 都声称支持 PXE:Intel PRO/100+ (PILA8460BNG1)、3Com 3C905CX-TX-M、D-Link DFE-550TX、Linksys LNE100TX 和 SMC 1255TX(Tulip 芯片组)。在这五种卡中,只有 3Com 卡开箱即用。我能够为 SMC 卡单独获得一个引导 ROM,之后它也能正常工作。其他三张卡都有明显的但空置的引导 ROM 插槽,这些插槽默认情况下未发货。买者自慎。
当 PXE NIC 被主板 BIOS 选为启动设备时,它会在 LAN 上广播 DHCP 请求,并在收到的响应中查找 PXE 扩展。如果它收到包含其中一些扩展的响应,则它会确认并接受该响应。特别是,它尊重服务器响应中的 next-server 和 filename 参数。这些参数指定 TFTP 服务器的 IP 地址以及包含客户端应下载并启动的引导加载程序的文件名。
Internet 软件联盟的版本 3.0 DHCP 服务器可以配置为通告 PXE 扩展,它是随许多 Linux 发行版(包括 Red Hat 8.0 和更高版本)一起提供的 DHCP 服务器。列表 1 是一个 DHCP 服务器配置文件 dhcpd.conf 的示例,当 DHCP 客户端将自身标识为 PXE NIC 时,该文件会生成带有 PXE 扩展的 DHCP 响应。使用此配置,客户端从位于 192.168.1.1 的 TFTP 服务器下载文件 pxelinux.0。表 1 列出了配置文件中设置的选项。
列表 1. 支持 PXE 客户端的 dhcpd.conf 文件示例
option space PXE; option PXE.mtftp-ip code 1 = ip-address; option PXE.mtftp-cport code 2 = unsigned integer 16; option PXE.mtftp-sport code 3 = unsigned integer 16; option PXE.mtftp-tmout code 4 = unsigned integer 8; option PXE.mtftp-delay code 5 = unsigned integer 8; option PXE.discovery-control code 6 = unsigned integer 8; option PXE.discovery-mcast-addr code 7 = ip-address; subnet 192.168.1.0 netmask 255.255.255.0 { class "pxeclients" { match if substring (option vendor-class-identifier, 0, 9) = "PXEClient"; option vendor-class-identifier "PXEClient"; vendor-option-space PXE; # At least one of the vendor-specific PXE # options must be set in order for the client # boot ROMs to realize that this is a PXE- # compliant server. We set the MCAST IP address # to 0.0.0.0 to tell the boot ROM that we can't # provide multicast TFTP. option PXE.mtftp-ip 0.0.0.0; # This is the name of the file the boot ROMs # should download. filename "pxelinux.0"; # This is the name of the server they should # get it from. next-server 192.168.1.1; } pool { max-lease-time 86400; default-lease-time 86400; range 192.168.1.2 192.168.1.254; # If you include this, you must provide host # entries for every client, optionally associating # ethernet MAC addresses with IP addresses. # deny unknown clients; } }
显然,必须将 192.168.1.1 的服务器配置为提供 TFTP 服务。它还必须有一个名为 pxelinux.0 的引导加载程序映像,TFTP 服务器进程会在其中查找它(通常在 /tftpboot 目录中)。TFTP 服务器进程通常由超级服务器 inetd 或 xinetd 之一管理,因此启用它意味着要修改它们的配置文件之一(分别为 /etc/inetd.conf 或 /etc/xinetd.conf)。
文件 pxelinux.0 是来自 H. Peter Anvin 的 SYSLINUX 项目的引导加载程序。与通用引导加载程序(如 LILO 或 GRUB)不同,PXELINUX 了解 PXE 协议,并且具有必要的网络功能,可以通过使用 TFTP 下载内核和压缩的 RAM 磁盘来在此处接管引导过程。但是,PXELINUX 需要增强的 TFTP 服务器,该服务器应了解 TSIZE 选项 (RFC 2349)。幸运的是,H. Peter Anvin 还提供了一个修改后的标准 BSD TFTP 守护程序版本,名为 tftp-hpa,它确实支持此选项。最简单的方法是将标准 TFTP 守护程序(通常位于 /usr/sbin/in.tftpd)替换为 tftp-hpa。
PXELINUX 知道 PXE 引导 ROM 将来自 DHCP 服务器响应的网络参数存放在内存中的位置,并且可以使用这些参数启动另一个 TFTP 会话,以从服务器下载其配置文件。在 TFTP 服务器配置如上所述的情况下,客户端上运行的引导加载程序首先尝试在 /tftpboot/pxelinux.cfg/ethermac 中查找其配置文件,其中 ethermac 表示客户端的以太网硬件地址,采用小写十六进制,八位字节之间用连字符分隔,例如 fe-ed-de-ad-be-ef。如果失败,引导加载程序将尝试 /tftpboot/pxelinux.cfg/iphex,其中 iphex 是客户端的 IP 地址,采用大写十六进制。例如,如果客户端的 IP 地址为 192.168.0.12,则 PXELINUX 将尝试下载文件 /tftpboot/pxelinux.cfg/C0A8000C。如果该文件不存在,则从名称中删除最低有效半字节,然后重复该过程。因此,在上面的示例中,在找不到 C0A8000C 之后,PXELINUX 将尝试 C0A8000,然后 C0A800,依此类推。这使得为整个子网使用单个配置文件成为可能,前提是子网边界与半字节对齐。
列表 2 显示了 PXELINUX 配置文件的内容。第一行给出了一个文件的名称,该文件包含要下载的压缩内核映像 - 所有路径都相对于服务器上的 /tftpboot。第二行列出了应传递给内核的参数,指定根文件系统应为 64MB RAM 磁盘,应以读/写方式挂载。最后一行导致 PXELINUX 生成一个附加的内核参数,其中包含ip=client-ip:server-ip:gateway-ip:netmask并使用 PXE 引导 ROM 从 DHCP 服务器的响应中获得的值。如果内核是在启用了 IP 网络内核级自动配置的情况下构建的,这将非常有用。如果是这样,当内核启动时,它会使用这些参数来配置网络接口,从而无需在启动脚本中使用 ifconfig 或 ifup 来执行此操作。
列表 2. PXELINUX 配置文件指定要下载的压缩内核映像。
DEFAULT vmlinuz APPEND initrd=ramdisk.gz ramdisk=65536 root=/dev/ram rw IPAPPEND 1
为了使用 IP 参数的内核级自动配置,网络设备驱动程序必须在启动早期就可用,甚至在挂载根文件系统之前。因此,它不能是可加载模块。由于大多数发行版附带的内核广泛使用可加载模块,因此实际上这意味着有必要为 X 终端构建自定义内核。此外,自定义内核应支持 RAM 磁盘和初始 RAM 磁盘。IP 网络内核级自动配置也很方便。没有必要包含任何获取 IP 地址的动态方法(可以选择 DHCP、BOOTP 和 RARP),但是,由于 PXELINUX 配置文件中包含的 IPAPPEND 行确保内核接收到正确的 IP 参数。最后,设备文件系统支持以及启动时自动挂载的 devfs 大大简化了 RAM 磁盘根文件系统中的 /dev 目录。
如果不是因为 Richard Gooch 的设备文件系统和 Erik Anderson 的 BusyBox 组合二进制文件的出现,那么构建和填充根文件系统将是设置无盘 Linux 盒最复杂的部分。设备文件系统自动管理 /dev 目录,根据内核中加载的设备需要创建设备入口点。这意味着两件事:目录中没有不必要的条目,RAM 磁盘根文件系统的构建者不必花费数小时使用 mknod 尝试创建所有需要的设备入口点。BusyBox 组合二进制文件是一个可执行文件,它根据其调用方式更改其个性。通常的方法是从 /bin/ls、/bin/cat、/bin/ps、/sbin/mount 等创建指向 /bin/busybox 的符号链接,以创建一个极简主义的 UNIX 系统。不需要额外的库或二进制文件;BusyBox 将它们整合到一个文件中。
考虑此设置的一种方法是,设备文件系统负责 /dev;BusyBox 负责 /bin 和 /sbin;内核负责 /proc;只读 NFS 挂载负责 /usr;/tmp 可以为空。因此,您需要担心的只是 /etc。实际上,/etc 可以非常简约,仅包含 /etc/fstab、/etc/inittab 和 /etc/init.d/rcS,后者是 BusyBox 作为 init 运行时使用的单个启动脚本。
BusyBox 是为嵌入式 Linux 世界编写的,通常会构建为静态可执行文件。但是,XFree86 服务器本身依赖于许多通常在 /lib 中找到的共享库。我们将要 NFS 挂载 /usr,因此我们不必担心在 /usr/lib 中找到的共享库,但我们确实必须提供 XFree86 希望在 /lib 中找到的共享库。因此,我们不妨利用通过将 BusyBox 配置为动态可执行文件而实现的节省空间。运行以下命令可以发现 /lib 中运行 XFree86 所需的最小库ldd /usr/X11R6/bin/XFree86。它们是 glibc (libc.so 和 libm.so)、PAM (libpam.so 和 libpam_misc.so) 以及动态加载器本身 (libdl.so 和 ld-linux.so)。
XFree86 可执行文件通常在 /usr/X11R6/bin 中找到,它是 /usr 的子目录。那么我们不需要在 RAM 磁盘中提供 X 服务器,而是可以从 NFS 挂载中获取它。虽然模块化 XFree86 服务器本身自大约 4.0 版本以来就不是特定于硬件的,但其配置文件肯定是。如果您管理着具有不同视频硬件的多个 X 终端,则不可能对所有终端使用相同的 XF86Config 文件。因此,我们不希望将其保存在 RAM 磁盘根文件系统中,它通常会在 /etc/X11/XF86Config 中找到。相反,我们可以使用存储在 NFS /usr 目录中的每个终端的配置文件。最终,BusyBox init 进程被配置为连续重生一个 shell 脚本,其中包含单行
/usr/X11R6/bin/XFree86 \ -xf86config /usr/X11R6/configs/iphex -query \ server
其中 iphex 是客户端的 IP 地址,采用十六进制(借用自 PXELINUX 的命名约定),server 是服务器的 IP 地址,采用点分十进制。通过一些巧妙的 awk-on-/proc/cmdline 技巧,我们可以完全避免将任何主机名或 IP 地址硬编码到 RAM 磁盘映像中。
可以通过在终端上运行以下命令来创建基本的 XFree86 配置文件XFree86 -configure。一般来说,这可以正确识别视频硬件,并且生成的配置文件会加载适当的 XFree86 模块。但是,值得一提的是,默认的指针设备 /dev/mouse 通常在使用设备文件系统的系统上不存在。例如,PS/2 鼠标在 /dev/misc/psaux 中找到。
使 X 终端成为 X 终端而不是带有图形显示的 Linux 盒子的部分是-query server上面 XFree86 命令行的一部分。这会导致终端上的 XFree86 服务器运行 XDMCP(X 显示管理器控制协议)会话,以尝试让服务器管理其显示。但是,并非每个服务器都会同意这样做。
首先,也是最明显的,服务器必须侦听传入的 XDMCP 连接。XDMCP 通常在 UDP 端口 177 上,并且大多数显示管理器(xdm、gdm、kdm)都可以配置为侦听 XDMCP 请求。虽然大多数发行版都配置为在启动时运行显示管理器,但出于安全考虑,大多数发行版都不侦听传入的 XDMCP 请求。例如,经典的 X 显示管理器 xdm 通常随行一起分发
DisplayManager.requestPort: 0
在其配置文件(通常为 /etc/X11/xdm/xdm-config)中。必须注释掉此行,xdm 才能接受 XDMCP 请求。此外,可以使用配置文件 /etc/X11/xdm/Xaccess(不要与 /etc/X11/xdm/Xservers 混淆,后者在很大程度上是历史遗留物)将 xdm 限制为每个主机或每个子网的连接。例如,要将 xdm 限制为 192.168.1.0/24 子网中的终端,请在 /etc/X11/xdm/Xaccess 的末尾添加一行,其中仅包含 192.168.1.0/24。
此外,如果服务器还通过 X 字体服务器进程 xfs 向终端提供字体,则会很方便。再次强调,虽然大多数发行版都运行字体服务器进程,但通常配置为不侦听传入的请求。例如,xfs 的配置文件 /etc/X11/fs/config 通常包含行no-listen = tcp。如果注释掉此行,则终端的 XF86Config 文件(存储在服务器上的 /usr/X11R6/configs/iphex 中)的 Files 部分可以仅包含一个 FontPath 而不是通常的六个,如列表 3 所示(其中假定服务器 IP 为 192.168.1.1)。
列表 3. 终端 XF86Config 片段
Section "Files" RgbPath "/usr/X11R6/lib/X11/rgb" ModulePath "/usr/X11R6/lib/modules" FontPath "tcp/192.168.1.1:7100" EndSection
最后,必须将服务器配置为将 /usr 文件系统以只读方式 NFS 导出到终端,因为终端从这里获取 XFree86 服务器。
运行 X 终端时,应牢记一些安全注意事项。首先,应该很明显,对 xdm 和 xfs 配置所做的更改正在撤消为提高服务器安全性而完成的操作。此外,本文中描述的设置不加密任何流量。终端上的每个击键都通过未加密的网络传输。运行 X 终端的唯一合理安全的方式是将它们全部放在仅供 X 终端使用的专用 LAN 上,并且该 LAN 不路由到 Internet。终端和服务器上的一个接口应该是终端 LAN 上唯一的接口。
由于印刷媒体的空间限制,本文从较高层面介绍了如何配置 Linux 盒子以无盘启动并成为 X 终端,而没有详细介绍精确的实现。鼓励感兴趣的读者从作者的网站下载 X 终端套件;它包括 shell 脚本、Makefile 和 README,以指导您完成有时很复杂的过程。此外,本文中描述的软件来自互联网上的众多资源,所有这些资源都提供了有关其特定软件包的更详细信息。请参阅在线资源以获取指针。
Chip Coldwell (coldwell@physics.harvard.edu) 是哈佛大学物理系的系统管理员。当他不摆弄各种计算机时,他通常会骑自行车或享受未婚妻 Cindy 的陪伴。