通过NFS(nfsroot)挂载根文件系统

作者:

由 Gero Kuhlmann <gero@gkminix.han.de> 于 1996 年编写

由 Martin Mares <mj@atrey.karlin.mff.cuni.cz> 于 1997 年更新

由 Nico Schottelius <nico-kernel-nfsroot@schottelius.org> 于 2006 年更新

由 Horms <horms@verge.net.au> 于 2006 年更新

由 Chris Novakovic <chris@chrisn.me.uk> 于 2018 年更新

为了使用无盘系统,例如X终端或打印服务器,根文件系统必须存在于非磁盘设备上。这可以是initramfs(参见Ramfs、rootfs和initramfs),ramdisk(参见使用初始RAM盘(initrd))或通过NFS挂载的文件系统。以下文本描述了如何将NFS用于根文件系统。在本文的其余部分中,“客户端”指无盘系统,“服务器”指NFS服务器。

启用nfsroot功能

为了使用nfsroot,在配置过程中需要选择将NFS客户端支持作为内置功能。一旦选择此项,nfsroot选项将可用,也应将其选中。

在网络选项中,可以选择内核级别的自动配置,以及支持的自动配置类型。同时选择DHCP、BOOTP和RARP是安全的。

内核命令行

当内核被引导加载程序(见下文)加载后,需要告知它使用哪个根文件系统设备。对于nfsroot,还需要告知服务器位置以及服务器上要挂载为根目录的名称。这可以通过使用以下内核命令行参数来建立:

root=/dev/nfs

这是启用伪NFS设备所必需的。请注意,它不是一个真实的设备,而只是一个告诉内核使用NFS而非真实设备的同义词。

nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>]

如果命令行中未提供nfsroot参数,将使用默认值"/tftpboot/%s"

<server-ip> 指定NFS服务器的IP地址。

默认地址由ip参数(见下文)决定。此参数允许为IP自动配置和NFS使用不同的服务器。

<root-dir> 服务器上要挂载为根目录的名称。

如果字符串中存在“%s”标记,它将被客户端IP地址的ASCII表示替换。

<nfs-options> 标准NFS选项。所有选项用逗号分隔。

使用以下默认值:

port            = as given by server portmap daemon
rsize           = 4096
wsize           = 4096
timeo           = 7
retrans         = 3
acregmin        = 3
acregmax        = 60
acdirmin        = 30
acdirmax        = 60
flags           = hard, nointr, noposix, cto, ac
ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>:<dns0-ip>:<dns1-ip>:<ntp0-ip>

此参数告诉内核如何配置设备的IP地址以及如何设置IP路由表。它最初被称为nfsaddrs,但现在启动时的IP配置独立于NFS工作,因此它被重命名为ip,旧名称保留作为兼容性别名。

如果内核命令行中缺少此参数,则所有字段都假定为空,并应用下面提到的默认值。通常这意味着内核会尝试使用自动配置来配置所有内容。

<autoconf> 参数可以单独作为ip参数的值出现(前面不带所有‘:’字符)。如果值为“ip=off”或“ip=none”,则不会进行自动配置,否则将进行自动配置。最常见的用法是“ip=dhcp”。

<client-ip> 客户端的IP地址。

默认值:通过自动配置确定。

<server-ip> NFS服务器的IP地址。

如果使用RARP确定客户端地址,并且此参数不为空,则只接受来自指定服务器的回复。

仅NFS根文件系统需要。也就是说,如果缺少此参数且NFS根文件系统未在运行,则不会触发自动配置。

值将以“bootserver ”为前缀导出到 /proc/net/pnp(见下文)。

默认值:通过自动配置确定。使用自动配置服务器的地址。

<gw-ip> 如果服务器在不同的子网,则为网关的IP地址。

默认值:通过自动配置确定。

<netmask> 本地网络接口的子网掩码。

如果未指定,则子网掩码将根据客户端IP地址(假设为有类地址)推导得出。

默认值:通过自动配置确定。

<hostname> 客户端名称。

如果存在“.”字符,则第一个“.”之前的部分用作客户端的主机名,之后的部分用作其NIS域名。它可以由自动配置提供,但其缺失不会触发自动配置。如果指定且使用DHCP,则用户提供的主机名(以及NIS域名,如果存在)将包含在DHCP请求中;这可能会导致为客户端创建或更新DNS记录。

默认值:客户端IP地址以ASCII表示法使用。

<device> 要使用的网络设备名称。

默认值:如果主机只有一个设备,则使用该设备。否则,设备通过自动配置确定。这是通过向所有设备发送自动配置请求,并使用第一个收到回复的设备来完成的。

<autoconf> 自动配置使用的方法。

对于指定多个自动配置协议的选项,将使用所有协议发送请求,并使用第一个回复的协议。

无论此选项的值如何,只会使用已编译到内核中的自动配置协议。

off or none: don't use autoconfiguration
              (do static IP assignment instead)
on or any:   use any protocol available in the kernel
             (default)
dhcp:        use DHCP
bootp:       use BOOTP
rarp:        use RARP
both:        use both BOOTP and RARP but not DHCP
             (old option kept for backwards compatibility)

如果使用dhcp,客户端标识符可以使用以下格式“ip=dhcp,client-id-type,client-id-value”

默认值:any

<dns0-ip> 主域名服务器的IP地址。

值将以“nameserver ”为前缀导出到 /proc/net/pnp(见下文)。

默认值:如果未使用自动配置则为无;如果使用自动配置则自动确定。

<dns1-ip> 辅助域名服务器的IP地址。

参见 <dns0-ip>。

<ntp0-ip> 网络时间协议 (NTP) 服务器的IP地址。

值将导出到 /proc/net/ipconfig/ntp_servers,但除此之外未被使用(见下文)。

默认值:如果未使用自动配置则为无;如果使用自动配置则自动确定。

配置(无论是手动还是自动)完成后,将创建两个文件,格式如下;如果相应值在配置后为空,则省略该行

  • /proc/net/pnp

    #PROTO: <DHCP|BOOTP|RARP|MANUAL> (取决于配置方法) domain <dns-domain> (如果自动配置,则为DNS域) nameserver <dns0-ip> (主域名服务器IP) nameserver <dns1-ip> (辅助域名服务器IP) nameserver <dns2-ip> (第三域名服务器IP) bootserver <server-ip> (NFS服务器IP)

  • /proc/net/ipconfig/ntp_servers

    <ntp0-ip> (NTP服务器IP) <ntp1-ip> (NTP服务器IP) <ntp2-ip> (NTP服务器IP)

<dns-domain> 和 <dns2-ip>(在 /proc/net/pnp 中)以及 <ntp1-ip> 和 <ntp2-ip>(在 /proc/net/ipconfig/ntp_servers 中)在自动配置期间被请求;它们不能作为“ip=”内核命令行参数的一部分指定。

由于“domain”和“nameserver”选项被DNS解析器识别,因此在使用NFS根文件系统的系统上,/etc/resolv.conf 通常链接到 /proc/net/pnp。

请注意,内核不会将其发现的任何NTP服务器同步系统时间;这是用户空间进程的责任(例如,如果真实根文件系统在NFS上,initrd/initramfs脚本会在挂载真实根文件系统之前将 /proc/net/ipconfig/ntp_servers 中列出的IP地址传递给NTP客户端)。

nfsrootdebug

此参数启用在启动时在内核日志中显示调试消息,以便管理员可以验证正确的NFS挂载选项、服务器地址和根路径是否传递给了NFS客户端。

rdinit=<executable file>

为了指定哪个文件包含启动系统初始化的程序,管理员可以使用此命令行参数。此参数的默认值为“/init”。如果指定的文件存在且内核可以执行它,则与根文件系统相关的内核命令行参数(包括“nfsroot=”)将被忽略。

挂载根文件系统的过程描述可以在早期用户空间支持中找到。

引导加载程序

要将内核加载到内存中,可以使用不同的方法。它们取决于各种可用功能

  • 使用syslinux从软盘引导

    构建内核时,创建使用syslinux的引导软盘的简单方法是使用zdisk或bzdisk make目标,它们分别使用zimage和bzimage镜像。这两个目标都接受FDARGS参数,可用于设置内核命令行。

    例如

    make bzdisk FDARGS="root=/dev/nfs"
    

    请注意,运行此命令的用户需要有权访问软盘驱动器设备,即 /dev/fd0。

    有关syslinux的更多信息,包括如何为预构建的内核创建引导盘,请参见https://syslinux.zytor.com/

    注意

    以前,可以直接使用dd将内核写入软盘,使用rdev配置引导设备,并使用生成的软盘引导。Linux不再支持这种引导方法。

  • 使用isolinux从光盘引导

    构建内核时,创建使用isolinux的可引导光盘的简单方法是使用isoimage目标,它使用bzimage镜像。与zdisk和bzdisk类似,此目标接受FDARGS参数,可用于设置内核命令行。

    例如

    make isoimage FDARGS="root=/dev/nfs"
    

    生成的ISO镜像将是 arch/<ARCH>/boot/image.iso。可以使用包括cdrecord在内的各种工具将其写入光盘。

    例如

    cdrecord dev=ATAPI:1,0,0 arch/x86/boot/image.iso
    

    有关isolinux的更多信息,包括如何为预构建的内核创建引导盘,请参见https://syslinux.zytor.com/

  • 使用LILO

    使用LILO时,所有必要的命令行参数都可以使用LILO配置文件中的“append=”指令指定。

    但是,要使用“root=”指令,您还需要创建一个虚拟根设备,该设备可以在LILO运行后删除。

    例如

    mknod /dev/boot255 c 0 255
    

    有关配置LILO的信息,请参阅其文档。

  • 使用GRUB

    使用GRUB时,内核参数只需在内核规范后附加即可:kernel <kernel> <parameters>

  • 使用loadlin

    loadlin可用于从DOS命令行引导Linux,而无需将本地硬盘挂载为根目录。本文作者尚未对此进行彻底测试,但一般来说,应该可以像配置LILO一样配置内核命令行。

    有关更多信息,请参阅loadlin文档。

  • 使用引导ROM

    这可能是引导无盘客户端最优雅的方式。使用引导ROM,内核通过TFTP协议加载。本文作者不知道有任何支持通过网络引导Linux的商业引导ROM。但是,有两个免费的引导ROM实现:netboot-nfs和etherboot,它们都可以在sunsite.unc.edu上找到,并且都包含引导无盘Linux客户端所需的一切。

  • 使用pxelinux

    Pxelinux可用于使用许多现代网卡上存在的PXE引导加载程序引导Linux。

    使用pxelinux时,内核镜像使用“kernel <relative-path-below /tftpboot>”指定。nfsroot参数通过添加到“append”行传递给内核。通常将串口控制台与pxelinux结合使用,有关更多信息,请参见Linux串口控制台

    有关isolinux的更多信息,包括如何为预构建的内核创建引导盘,请参见https://syslinux.zytor.com/

致谢

内核中的nfsroot代码和RARP支持由 Gero Kuhlmann <gero@gkminix.han.de> 编写。

IP层自动配置代码的其余部分由 Martin Mares <mj@atrey.karlin.mff.cuni.cz> 编写。

为了编写nfsroot的初始版本,我要感谢 Jens-Uwe Mager <jum@anubis.han.de> 的帮助。