作为一名 Linux? 管理员,您可能会在增加外部存储卷之后或者甚至在安装新 Linux 操作系统之后,尝试重新启动服务器时遇到诸如 cannot mount rootfs 和 kernel panic 之类的 rootfs 错误。本文将概要介绍 x86 平台上的 Linux 引导进程,显示出现此问题的原因,并提供避免或修正此问题的四个技巧。
问题概述
根文件系统(在样例错误消息中名为 rootfs)是 Linux 的最基本的组件。根文件系统包含支持完整的 Linux 系统所需的所有内容。它包含所有应用程序、配置、设备、数据等。没有根文件系统,您的 Linux 系统将无法运行。
在重新启动系统后,您可能会遇到 cannot mount rootfs 错误(在 Linux 主机得到 kernel panic 之后)。这种问题经常会在从外部存储中增加一些存储卷之后出现,也可能会在完成文件复制并需要重新启动来完成安装时出现。
如果出现此问题,您的系统将不会重新启动。诊断和修正此问题都需要花时间。本文介绍的 4 个技巧应当可以帮助您解决问题并节省时间。
另一个问题是 Linux 内核需要装入根文件系统,但是它找不到目标设备。换言之,根文件系统没有出现在它应该在的位置。例如,您把 Linux 根文件系统安装在 /dev/sda 磁盘中,但是您的系统在重新启动时无法装入它。出现这个问题有两个可能的原因:
1.磁盘 /dev/sda 在系统重新启动时没有显示。
出现这种情况的可能的原因是您的 Linux 主机没有装入根文件系统的关键驱动器。情况未必如此。Linux 安装程序将把需要的所有驱动器构建到 initrd 映像中,以便 Linux 系统可以在启动时轻松地载入设备驱动器。但是,如果您是手动安装了磁盘,则可能会出现此错误。
2.显示了磁盘 /dev/sda,但是它不是根文件系统。在重新启动后,您的根文件系统已经改为 /dev/sdb。
这是最可能出现的情况。
那么,系统重新启动后 /dev/sda 是如何被重命名为 /dev/sdb 的?在 Linux 中,/dev/sd* 表示 SCSI 设备。Linux 将把这些设备的名称从 sda 改为 sdz(以及从 sdaa 改为 sdzz,以此类推)。它将把第一个 SCSI 设备命名为 /dev/sda,把第二个 SCSI 设备命名为 /dev/sdb,以此类推。
如果使用在根文件系统设备适配器驱动器之前装入的驱动器(最初位于 /dev/sda)在适配器中安装设备,则根文件系统将移到命令链的下一个字母(/dev/sdb),并且根文件系统不是遇到的第一个设备。因此,如果它在行首找不到 rootfs,它就无法装入。
这是所发生情况的简单说明,但是为了给这个场景补充更多上下文,让我们概要说明一下 Linux 引导进程(如果您了解 “根本” 原因,请直接跳到 修正问题的四个技巧)。
Linux 引导的工作原理
引导加载程序
Linux Loader(LILO)是适用于 Linux 的通用引导加载程序,它不依赖于特定的文件系统,并且可以从软盘和硬盘引导操作系统。引导时,您最多可以从十六个不同映像中做出选择,并且可以分别为每个内核设置不同的参数(如根设备)。LILO 可以放在主引导记录(MBR)或分区的引导扇区中;如果执行第二个选项,还必须把另一些内容放到 MBR 中才能装入 LILO。
另一个引导加载程序是 GNU GRUB(GRUB)。GRUB 是多重引导规范(Multiboot Specification)的参考实现,它将允许用户在计算机中立即拥有多个不同的操作系统,以及选择要运行哪个操作系统。GRUB 是动态配置的,在启动时载入其配置并允许在引导时更改,例如选择不同的内核或初始的 RAM 磁盘。GRUB 具有高度可移植性,支持多种可执行格式,并且不依赖于几何参数转换。GRUB 可以从网络下载操作系统映像,因此它可以支持无磁盘系统。GRUB 支持先自动解压缩操作系统映像,然后再引导。而且不同于其他引导加载程序,它可以通过 GRUB 提示符直接与用户进行通信。
以下步骤将概要介绍 Linux 引导进程的工作原理:
1.计算机在启动时首先执行的是初始测试 POST(开机自检,Power On Self Test),它将测试多个设备,包括处理器、内存、显卡和键盘。此外,还将测试引导介质(硬盘、软盘和 CD-ROM)。在 POST 之后,ROM 中的加载程序将载入引导扇区,该扇区随后将从活动分区中载入操作系统。您可以通过编辑服务器 BIOS 来更改引导介质顺序。
2.引导扇区总是在同一个位置 — 引导设备的磁道 0、柱面 0、磁头 0。此扇区包含名为 loader 的程序(对于 Linux,它通常是 LILO 或 GRUB);实际上是此程序引导操作系统。加载程序不是安装在 MBR 中,就是安装在活动主分区的第一个扇区中。
3.如果服务器中安装了多个操作系统,您需要从引导加载程序菜单中选择需要引导的操作系统。如果安装了多个内核,您还可以在此菜单中选择要载入的内核。
4.然后,引导加载程序将解压缩并载入内核。内核将首先载入内核模块,然后检测硬件(软驱、硬盘、网络适配器等),检验硬件配置,然后扫描和载入设备驱动程序。
5.在此阶段,内核将装入根文件系统和系统文件。在重新编译期间,可以(或通过其他程序)配置系统文件的位置。如果载入失败,kernel panic 将出现,并且系统将冻结(freeze)。这是先前提到的载入失败类型。
6.接下来,内核将启动系统初始化进程 init,这将成为第一个进程。随后它将启动系统的其余部分。init 进程是 Linux 的第一个进程,它是所有其他进程的父进程。此进程是在任何 Linux/UNIX? 系统中第一个运行的进程;它的 PID 始终为 1。
7.然后,init 将检验 /etc/inittab 文件以确定必须启动哪些进程。此文件将提供关于 runlevel 及在各个 runlevel 上应当启动的进程的 init 信息。然后,init 将查找第一个带有 sysinit(系统初始化)操作的行,然后将执行指定的命令文件,如 Red Hat Linux 中的 /etc/rc.d/rc.sysinit。在执行 /etc/rc.d/rc.sysinit 中的脚本之后,init 将开始启动与初始 runlevel 关联的进程。在执行完 runlevel 初始脚本时,Linux 将允许您登录。
讨论的每种解决方案都将处理这张列表中的第 5 步。
四个技巧
由于 cannot mount rootfs 错误主要是由设备顺序造成的,更改设备顺序或者更改驱动器加载顺序将解决此问题。
这两种方法可以通过以下四种方式实现(每种方法的目的都是让 Linux 根磁盘首先出现在内核/系统中,这样它可以总是使用 /dev/sda 作为设备名称):
1.更改服务器 BIOS 中的 PCI 设备引导顺序。
2.将根文件系统磁盘插入到适配器的第一个插槽中。
3.编辑 initrd 映像,以便首先载入根文件系统适配器驱动程序,然后再载入所有其他存储适配器驱动程序。
4.对根文件系统挂载使用标签、UUID 或友好的名称而非设备名称。
1. 更改服务器 BIOS 中的 PCI 设备引导顺序
如果使用的是 IBM System x? 或 IBM BladeCenter? HS 系列,则过程为:
1.当服务器输出自检信息并提示您 Press F1 to enter BIOS 时按 F1 键。
2.选择 Start Options 并按 Enter 键,如图 1 所示。
图 1. 配置/设置工具
3.如果您的 Linux 安装在本地 Planar SAS 磁盘中,请将 PCI Device Boot Priority 更改为根文件系统磁盘正在使用的适配器(如下面的图 2 所示)。
图 2. 启动选项
或者,您可以选择相关的 PCI 插槽以让 Daughter Card Slot 1 首先以最低的设备 ID 显示给系统(参见图 3)。
图 3. 另一组启动选项
在本例中,您的本地 Planar SAS 将有较大的设备 ID。如果把 Linux 安装到设备名称为 /dev/sda 的 Planar SAS 磁盘,然后把一个新磁盘设备附加到 Daughter Card Slot 1 上,则此新磁盘将使用设备名称 /dev/sda,并且根文件系统磁盘将被改为 /dev/sdb。这将导致 cannot mount rootfs 错误。
2. 将根文件系统磁盘插入到适配器的第一个插槽中
如果根文件系统磁盘适配器可以附加两个以上的设备,或者您有两个以上的磁盘适配器,请确保根文件系统磁盘位于设备 ID 最低的插槽中(例如第一个适配器的第一个插槽)。如果有一个新添加的磁盘拥有比根文件系统磁盘更低的 ID,则需要把根文件系统磁盘置于前一个插槽中。这将使根文件系统磁盘首先显示给 Linux 系统。
3. 编辑 initrd 映像以更改存储适配器顺序
initrd 映像实际上是一个 RAM 磁盘,其中包含带有基本配置文件、二进制文件、库和驱动程序的小型文件系统。在这个小型文件系统中,有一个将在引导期间装入到内存中的根文件系统,还有一个用于装入系统设备驱动程序的初始化脚本;它将在加载完所有设备驱动程序后重新加载实际的根文件系统磁盘。当系统尝试将 initrd 根文件系统切换到拥有此配置中的设备名称的实际根文件系统磁盘时,经常会出现 cannot mount rootfs 错误。您需要更改 initrd 映像的驱动程序载入脚本中的设备驱动程序载入顺序,让您的根文件系统磁盘在系统尝试重新加载实际的根文件系统之前 显示正确的设备名称。为此:
1.使用 Linux 恢复 CD 将系统引导到恢复模式下,因为您无法访问根文件系统。
2.将根文件系统加载到诸如 sysroot 之类的临时目录中(某些 Linux 恢复 CD 将自动为您执行此操作)。例如,在将系统引导到恢复模式下并且根文件系统使用的是第 3 分区时,根文件系统磁盘将被识别为 /dev/sdc。
[root@linuxhost ~]#mount /dev/sdc3 sysroot
3.将恢复系统根目录切换为 sysroot,以便您可以访问问题服务器系统的根文件系统。
[root@linuxhost ~]#chroot sysroot
4.提取 initrd 映像。
a.对于 Red Hat Enterprise Linux 第 3 版之前的版本和 SUSE Linux Enterprise Server 第 9 版之前的版本:
[root@linuxhost ~]#cp /boot/initrd-x.x.x-x.ELsmp.img ./initrd.gz
[root@linuxhost ~]#mkdir temp
[root@linuxhost ~]#gunzip ./initrd.gz
[root@linuxhost ~]#mount -o loop -t ext2 initrd temp
这段代码将把 initrd 映像提取到 initrd 文件中;然后将使用 ext2 文件系统类型加载这个文件,以加载 temp。在此之后,您将看到所有文件被封装到 initrd 映像中。
b.对于 Red Hat Enterprise Linux 第 4 版和更高版本或者 SUSE Linux Enterprise Server 第 10 版和更高版本:
[root@linuxhost ~]#cp /boot/initrd-*.img ./initrd.gz
[root@linuxhost ~]#mkdir temp
[root@linuxhost ~]#cd temp
[root@linuxhost ~]#gzip -dc ../initrd.gz | cpio -id
在这些最新的 Linux 发行版中,initrd 映像也是用 cpio 压缩的。这些命令将提取 temp 目录下包含目录结构的整个文件系统。您现在应当可以在 temp 目录的 initrd 映像中找到所有文件。
5.现在,您将在 temp 目录下找到适用于 Red Hat Enterprise Linux 和 SUSE Linux Enterprise Server 第 10 版的 init 文件,或者适用于 SUSE Linux Enterprise Server 第 9 版之前的版本的 linuxrc 文件。此文件是包含将设备驱动程序载入到系统内存中的所有命令的简单 Linux shell 脚本。您可以通过文本编辑器或者其他编辑器来编辑它。
6.编辑 init 或 linuxrc 文件,以使根文件系统磁盘适配器出现在同一个类别中的所有其他存储适配器的前面。在大多数情况下,根文件系统磁盘将是带有 sd 前缀的 SCSI 设备;您需要把诸如 FC 或 SAS HBA 驱动程序之类的所有其他存储适配器放到根文件系统磁盘所依赖的驱动程序之后。
a.例如,如果根文件系统的物理磁盘与 SAS 适配器连接在一起,则 init 文件可以有如下所示的字段:
…….
echo “Loading mptbase.ko module”
insmod /lib/mptbase.ko
echo “Loading mptscsi.ko module”
insmod /lib/mptscsi.ko
echo “Loading mptspi.ko module”
insmod /lib/mptspi.ko
echo “Loading mptsas.ko module”
insmod /lib/mptsas.ko
echo “Loading mptscsih.ko module”
insmod /lib/mptscsih.ko
echo “Loading qla2xxx.ko module”
insmod /lib/qla2xxx.ko
…….
在本例中,将所有其他 SCSI 适配器驱动程序(如 Qlogic HBA 驱动程序 qla2xxx.ko)放在 SAS 适配器驱动程序 mptsas.ko 之后。
b.如果使用的是 SUSE Linux Enterprise Server 第 10 版,它将类似以下代码:
modprobe scsi_mod $params
modprobe sd_mod $params
params=
for p in $(cat /proc/cmdline) ; do
case $p in
aacraid.*)
params=”$params ${p#aacraid.}”
;;
esac
done
echo “Loading aacraid”
modprobe aacraid $params
modprobe scsi_transport_fc $params
modprobe firmware_class $params
params=
for p in $(cat /proc/cmdline) ; do
case $p in
qla2xxx.*)
params=”$params ${p#qla2xxx.}”
;;
esac
done
echo “Loading qla2xxx”
modprobe qla2xxx $params
7.检验您的版本是否正确,这样才不会遗漏任何所需的驱动程序(例如,SAS 需要同时装入 mptbase.ko、mptscsi.ko、mptspi.ko、mptsas.ko 和 mptscsih.ko,这样您不能先装入 qla2xxx.ko,再装入 mptscsih.ko)。此外,您可能还需要检查错误的输入。
8.此时,您需要将 initrd 文件系统重新压缩到一个映像中,并替换 /boot 目录下的原有映像。
a.对于 Red Hat Enterprise Linux 第 3 版之前的版本和 SUSE Linux Enterprise Server 第 9 版之前的版本:
[root@linuxhost ~]#umount temp
[root@linuxhost ~]#gzip initrd
[root@linuxhost ~]#cp initrd.gz /boot/initrd-2.x.x-x.img
b.对于 Red Hat Enterprise Linux 第 4 版和更高版本或者 SUSE Linux Enterprise Server 第 10 版和更高版本:
[root@linuxhost ~]#find ./ | cpio -H newc -o > ../initrd
[root@linuxhost ~]#gzip initrd
[root@linuxhost ~]#cp initrd.gz /boot/initrd-2.6.x-x.img
9.祝贺您!您已经修正了问题。现在重新启动并检查结果。
4. 使用标签、UUID 或友好的名称
UUID
UUID 表示全局惟一标识符(Universally Unique Identifier)。它是软件构造中使用的标识符标准,开放软件基金会(Open Software Foundation)将其标准化为分布式计算环境(Distributed Computing Environment,DCE)的一部分。UUID 的目的是支持分布式系统,以惟一地标识信息而无需进行重大的协调;标有 UUID 的信息可以并入单个数据库中,而无需解决名称冲突。此标准的重要应用包括 Linux ext2/ext3 文件系统、LUKS 加密分区、GNOME、KDE、Mac OS X 和 Microsoft? Globally Unique Identifiers。
某些 Linux 文件系统类型(如 ext2、ext3、reiserfs、swap 和 XFS)支持装入带有标签的文件系统而不是带有设备名称的文件系统,如果您的 Linux 系统支持,则可以转而使用 UUID。此外,如果您的设备驱动程序支持,则可以使用友好的名称。
这些方法需要 Linux 系统支持这些功能(如 Red Hat Enterprise Linux V4 和更高版本或者 SUSE Linux Enterprise Server V9 和更高版本)。由于标签、UUID 和友好的名称将永远与专用设备绑定在一起,因此不管拥有什么设备 ID 或者使用什么设备名称,系统总是能够找到根文件系统磁盘。
使用标签
1.在创建诸如 root、swap 之类的文件系统或其他文件系统时,创建一个标签。
[root@linuxhost ~]#mkfs.ext3 -L ROOT /dev/sda1
[root@linuxhost ~]#mkfs.reiserfs -l OSROOT /dev/sdb2
[root@linuxhost ~]#mkfs.xfs -L XFSROOT /dev/sde3
[root@linuxhost ~]#mkswap -L SWAP0 /dev/sdb5
2.在创建文件系统后给它添加一个标签。
[root@linuxhost ~]#e2label /dev/sda1 PRIMARY_ROOT
[root@linuxhost ~]#reiserfstune -l OSROOT /dev/sdb2
[root@linuxhost ~]#xfs_admin -L DATA1 /dev/sdf4
3.在系统中使用标签。
a.编辑 Linux 系统的 /etc/fstab,这样系统将使用标签而非设备名称来装入文件系统。下面是 /etc/fstab 内容的简单示例:
LABEL=ROOT / ext3 defaults 1 1
LABEL=BOOT /boot ext3 defaults 1 2
LABEL=SWAP swap swap defaults 0 0
LABEL=HOME /home ext3 nosuid,auto 1 2
b.编辑 Linux 引导加载程序的 grub.conf。
title Linux
root (hd0,0)
kernel (hd0,0)/vmlinuz ro root=LABEL=ROOT rhgb quiet
initrd (hd0,0)/initrd-2.x.x-xx.img
使用 UUID
1.获得根设备的 UUID。让我们假定以下示例中的根文件系统位于 /dev/sda 磁盘设备中:
[root@linuxhost ~]#scsi_id -g -s -u /block/sda
2.使用以下命令检查带有从第 1 步中得到的设备 UUID 的根文件系统设备的 ID。
[root@linuxhost ~]#ls /dev/disk/by-id/<your device UUID>
3.在本例中,通过编辑 /etc/fstab 以在系统中使用 UUID:
/dev/disk/by-id/scsi-<your uuid>-part2 / ext3 defaults 1 1
使用友好的设备名称
如果将设备映射程序多路径(DMMP)工具用于多路径存储,则可以将友好的名称用于根文件系统以避免设备在重新启动后被 DMMP 重新编号。
1.获得根设备的 UUID。让我们假定根文件系统位于 /dev/sda 磁盘设备中。
[root@linuxhost ~]#scsi_id -g -s -u /block/sda
2.编辑 /etc/multipath.conf 以添加根文件系统设备的友好名称,如下所示:
multipaths {
multipath {
wwid <your disk UUID get from above command>
alias OSROOT
}
}
3.然后,在重新引导系统或重新载入 DMMP 后,根文件系统设备将拥有诸如 /dev/mapper/OSROOT 之类的设备名称(如果根文件系统使用的是此磁盘的分区 3,则将拥有 /dev/mapper/OSROOT-part3 或 /dev/mapper/OSROOTp3 设备名称)。
4.编辑 /etc/fstab 以将此友好名称用于此根文件系统设备,如下所示:
/dev/mapper/OSROOT-part3 / ext3 defaults 1 1
5.编辑 /etc/grub.conf 以在系统启动时使用此名称。
title Linux
root (hd0,0)
kernel (hd0,0)/vmlinuz ro root=/dev/mapper/OSROOT-part3 rhgb quiet
initrd (hd0,0)/initrd-2.x.x-xx.img
结束语
6.此时,您已经完成了全部工作。您可以重新启动并查看它是否工作。
本文演示了如何设置系统以避免或修正 Linux 系统的 cannot mount rootfs 错误,并且还提供了 Linux 系统的引导进程的背景信息。