ALERT! /dev/disk/by-uuid/xxxxxxxxx does not exist. Dropping to a shell!

以清华同方一体机安装为例

一、问题描述

1, 问题描述

 问题的现象是,清华同方一体机安装完成后,关机,启动,屏幕未进入桌面,屏幕打印如下信息。必现。

— Boot args (cat /proc/cmdline)
— Check rootdelay= (did the system wait long enough?)
— Check root= (did the system wait for the right device?)
— Missing modules (cat /proc/modules; ls /dev)
ALERT! /dev/disk/by-uuid/xxxxxxxxx does not exist. Dropping to a shell!

 

2, 问题复现步骤和各个阶段的现象说明

    2.1 准备清华同方一体机,或者其他更老启动速度更慢的机器,或者慢速linux usb启动盘

    2.2 安装方德桌面操作系统, 推荐版本是1.5 0822.0004

    2.3 安装完毕后,关机后开机。

    2.4 启动后未进入桌面,屏幕打印字符串,进入initrd环境,进入桌面失败

    3, 对比测试和复现条件说明

       在金刚组提供的清华同方一体上,该现象必现。其他机器暂未复现。 根据以往经验在启动较慢的服务器,linux usb系统盘,慢速启动机器也有可能出现

二、问题分析

1、第一阶段

起止时间:2016.12.14至2016.12.14

分析思路:

    该问题必现,分析打印信息,alert! /dev/disk/by-uuid/########### does not exist直接断言信息initrd通过UUID查找硬盘失败。

直接原因肯定是找不到硬盘UUID,找不到的原因,推测可能是UUID的值不正确,或者其他原因导致。

验证过程:

    进入u盘启动盘。

    $blkid/dev/sda1: UUID="e9399244-24c6-46bd-9786-051806f0f93f" TYPE="ext4" /dev/sda5: UUID="df7a3fc6-d2dc-4c85-a445-8bbc02ff7c84" TYPE="swap"

    ......

    对比硬盘的UUID与硬盘内的文件系统里grub内的UUID是否相同。发现两者相同,证明硬盘的UUID值是正确的。

分析结果:

    硬盘的UUID值正确,排除UUID错误的嫌疑。排除硬盘本身损坏的嫌疑。

 

2、第二阶段

起止时间:2016.12.14至2016.12.15

分析思路:
    经过第一阶段分析验证,原因指向initrdgrub,内核或者启动速度太慢导致加载UUID失败。修改硬盘加载方式,和增加挂载root分区等待时间来验证推测。

验证过程:

    1、启动到Grub阶段,修改grub参数root=UUID=XXXXXXX 这里,改成root=/dev/sdXY,然后按ctrl+x启动。(这里sdXY中的X代表一个字母,Y代表一个数字,通常是/dev/sda1啦。可以在live cd模式下通过fdisk -l来看都有哪些设备,以此来确定XY的值) 

    通过指定根分区为/dev/sda1方式启动,正常进入桌面。

    2、启动到Grub阶段,修改grub参数root=UUID=XXXXXXX 这里,添加rootdelay=60

rootdelay 作用— Time to delay before attempting to mount the root filesystem.

    通过延长根分区加载时等待时间方式启动,正常进入桌面。

分析结果:

    通过修改根分区加载时间或者修改根分区加载方式可修复这个问题

    直接原因内核处理UUID的加载方式时,对于一些设备需要更长时间初始化。Initrd加载完成,根文件系统通过UUID方式未成功挂载。

2、第三阶段

起止时间:2016.12.15至2016.12.16

分析思路:
    从内核代码分析initrd执行过程,挂载根文件系统的方式,从initrd分析根分区挂载过程。

验证过程:

    查阅资料,跟踪内核代码发现prepare_namespace()里面mount root fail才会导致本问题,发现在系统开始做mount root的时候,硬盘还没有被bind。同时也发现这个function里面有个全局变量root_delay。在这个函数被调用时会先做delay root_delay。然后又查找了一下这个变量被赋值的地方,发现是通过bootargs环境变量传入的。在bootargs里面加上rootdelay之后,系统就能够正常的mount root了。

    对于启动比较慢的盘设备,从盘驱动加载到盘设备真正可用大概需要几秒钟时间。如果将盘驱动编译进内核,内核通常不能成功访问设备中的文件系统。因为在内核访问设备时,盘设备通常没有初始化完毕。所以常规的做法是,在initrd 中加载盘驱动,然后休眠几秒钟,等待设备初始化完毕后再挂载盘设备中的文件系统。

内核有三种方式加载根文佳系统,(这一段摘自kernel-4.3/linux-4.3/Documentation/early-userspace/README)

    (a)将所有需要的设备和文件系统驱动程序编译到内核中,非initrd。 init / main.c:init()将调用prepare_namespace()来装载 最终根文件系统,根据 root = 选项和选项 init = 运行 一些其他的init二进制文件。

    (b)将一些设备和文件系统驱动程序构建成模块并存储在 initrd中。initrd必须包含一个二进制'/ linuxrc'加载这些驱动程序模块。安装最后的根文件系统就是通过linuxrc使用pivot_root系统调用完成。 initrd安装是通过内核prepare_namespace()函数执行。

    (c)使用initramfs。必须跳过对prepare_namespace()的调用。这意味着二进制必须完成所有的工作。这个二进制通过修改usr / gen_init_cpio.c或通过新的initramfs initrd格式(cpio归档文件)存储。它必须命名为“/ init”。这个二进制是负责做prepare_namespace()做的所有事情。为了保持向后兼容性,/ init二进制只会 通过initramfs cpio归档文件运行。如果不是这样,init / main.c:init()将运行prepare_namespace()来挂载最后的根并执行预定义的init二进制文件。

 

initrd相关代码的调用层次关系图:

 ALERT! /dev/disk/by-uuid/xxxxxxxxx does not exist. Dropping to a shell!_第1张图片

 

分析结果:

    验证结果证明通过UUID方式加载需要更长时间,系统的主线程运行完毕需要的时间较短。而硬盘的初始化是在启动过程的最后几步进行的,而且和硬件通讯所需的时间比较长。这就造成了在主线程已经跑完的时候硬盘还没有ready.

    HGJ系统Grub提供两种根分区加载方式,UUID方式和根分区方式,UUID方式需要内核执行prepare_namespace()函数完全初始化完成,使用根分区方式只要保“driver_probe_done() != 0”即mount操作成功即可。使用UUID的好处是分区被唯一标识,不会受分区调整的影响,对于linux usb类系统盘,应添加加载根文件系统等待时间。

三、问题分析结论

    出错原因是通过UUID查找根分区失败,根分区挂载有UUID方式和直接写根分区方式,UUID方式加载需要的时间要比根分区方式长UUID方式需要硬盘完全初始化,根分区方式只需要初始化过程中mount操作成功),此硬盘加载过慢,initrd执行结束,硬盘初始化未完成,导致挂载根分区失败。

四、解决方法

    内核的设计者已经为这种情况,提供对应的启动参数,修改grub参数即可。

    方案一,在加载根文件系统时添加超时等待:

    在/etc/default/grub中添加参数GRUB_CMDLINE_LINUX="rootdelay=10",使用pdate-grub命令更新grub菜单。

    方案二,使用根分区方式加载:

    在/etc/default/grub中添加参数GRUB_DISABLE_LINUX_UUID=true,使用update-grub命令菜单更新grub菜单。

你可能感兴趣的:(系统问题分析)