Linux内核的一些探索

这两天测试组里的服务器时踩的坑。浅浅记录一下探索过程,以防未来再度踩坑。

事情是这样的。最近这几天接到任务,需要帮忙测试组里的一台服务器,其间由于电源供电功率不足,我使用了一台主机的GPU供电线为另一台主机GPU进行了供电(这台主机原本有GPU)。测试结束后,我装回了原先的GPU,却发现这台机器启动后出现了显示器无输出、ssh无法连接的问题——由于显示器无输出,甚至我都不知道启动进行到了哪一步,遑论排查问题了。

这下可把我急坏了。好在,第二天在GPT老师和实验室师姐的帮助下,我们找到了重置主板BIOS设置的方法(即,扣除主板纽扣电池进行放电,并按照主机维护手册的说明,安装了主板CMOS复位跳线并短暂重启,之后移除CMOS复位跳线),总算解决了问题——终于在显示器上看见了输出,ssh登录也正常了。

然而问题接踵而来。虽然现在能进入系统,但是检测不到显卡驱动(在供电测试前,显卡驱动是正常的)。前面做测试时我们并没有动系统,按理说显卡驱动应该都还在呀,为什么突然检测不到了呢?这不太对劲。于是进行了一些更深入的挖掘,并终于了解到一些关于系统内核的知识点。

image.png

一、关于Linux启动顺序的一些知识点

Linux 启动过程大致如下:

  1. BIOS/UEFI 加载引导程序(GRUB):按下电源键时,计算机的 CPU 还无法直接运行操作系统,而是先执行主板上的 固件(Firmware),即 BIOS 或 UEFI。BIOS/UEFI 进行 POST(Power-On Self Test,自检),检测 CPU、内存、硬盘、显卡等是否正常。随后,查找 引导设备(通常是 SSD/HDD,或 U 盘/光盘),并从引导设备中读取引导扇区的内容——对Linux来说,引导扇区的内容通常是GRUB(GRand Unified Bootloader) ,这是 Linux 最常见的引导加载程序。
  2. **GRUB 选择内核并加载 initramfs**:GRUB 显示启动菜单(如 Advanced options for Ubuntu),交由用户选择内核(或自动选择默认内核)。随后,GRUB将依次加载内核( vmlinuz-<version> )和 initramfs(initrd.img-<version> ),并将控制权交给 Linux 内核。
  3. 内核启动,并执行 initramfs 内部的 init 脚本 : vmlinuz 是压缩后的内核,启动时会解压到内存中。内核必须能访问磁盘、USB、网络等设备,但此时文件系统未挂载,因此需要 initramfs 提供最小的运行环境。initramfs是一个临时的根文件系统,内部有一个 init 脚本,负责查找真正的根文件系统,并加载存储驱动。
  4. initramfs 挂载真正的根文件系统(如 /dev/sda2:当initramfs完成根文件系统的查找和存储驱动加载以后,就通过switch_root 命令将控制权交给真正的文件系统。
  5. 切换到根文件系统并启动系统服务:Linux 内核成功挂载真正的根文件系统后,开始执行用户空间的 init 进程(PID 1), 这个 init 进程是整个系统的第一个用户态进程,负责启动所有服务。对于现代Linux来说,init进程通常由 systemd 程序担任。 systemd 程序将按照 /etc/systemd/system/default.target 文件启动到对应的目标,并按照 /etc/systemd/system/ 里的 unit 文件启动各个系统服务。如此,就完成了Linux整个系统的启动。

多说一句,在Linux上可以使用 journalctl -b 查看启动日志。journalctl --list-boots 可以列出所有启动日志(其输出大致如下图)。如果某一次启动在这个日志中查询不到,一般可能的原因就是启动失败,直接连系统都没有进去。

image.png

(一)grub是干什么的

如下图:

image.png

(二)什么是initramfs

initramfs(Initial RAM Filesystem,初始 RAM 文件系统)是 Linux 启动过程中用于引导的一个临时文件系统,它的主要作用是在系统启动时,为内核提供必要的驱动和工具,以便正确挂载根文件系统并启动 Linux 进程。

initramfs 主要在 内核引导阶段 发挥作用。一旦系统成功挂载真正的根文件系统(如 /dev/sda2),initramfs 的任务就完成了,并且它会被 释放(discarded)

虽然正常情况下 initramfs 会被丢弃,但在以下特殊情况下,它可能会 一直保持在 RAM 中

  1. 根文件系统损坏或未找到(进入 “initramfs shell”)
    • 例如 /etc/fstab 配置错误,导致系统无法挂载 /,你可能会进入 initramfs 维护模式 ((initramfs) shell)。
  2. 系统采用了完全基于 initramfsrootfs
    • 某些嵌入式 Linux 设备(如路由器、IoT 设备)可能会使用 initramfs 作为主要文件系统,而不再挂载磁盘上的根文件系统。
  3. dracutrd.break 选项
    • 在 RHEL/CentOS 里,可以通过 rd.breakinitramfs 进入调试模式,以便手动修复系统。

二、查看和修改默认内核

(一)查看到系统中已存在的内核和模块

使用 ls -lhF /lib/modulesdpkg --list 指令,可以查看到系统中已存在的内核以及内核模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
(base) zhangwanyu@lih-workstation:~$ ls -lhF /lib/modules
total 16K
drwxr-xr-x 2 root root 4.0K Dec 26 06:09 5.15.0-124-generic/
drwxr-xr-x 5 root root 4.0K Nov 16 23:43 5.15.0-125-generic/
drwxr-xr-x 2 root root 4.0K Jan 7 06:10 5.15.0-127-generic/
drwxr-xr-x 5 root root 4.0K Jan 7 06:09 5.15.0-130-generic/
(base) zhangwanyu@lih-workstation:~$ dpkg --list | grep linux-image
rc linux-image-5.15.0-124-generic 5.15.0-124.134 amd64 Signed kernel image generic
ii linux-image-5.15.0-125-generic 5.15.0-125.135 amd64 Signed kernel image generic
rc linux-image-5.15.0-127-generic 5.15.0-127.137 amd64 Signed kernel image generic
ii linux-image-5.15.0-130-generic 5.15.0-130.140 amd64 Signed kernel image generic
ii linux-image-generic 5.15.0.130.128 amd64 Generic Linux kernel image
(base) zhangwanyu@lih-workstation:~$

我们可以看到,这个系统里面有4个不同版本的内核。

可以使用 ls /lib/modules/*/kernel/drivers/video/nvidia* 查询在哪些内核当中有GPU相关驱动:

1
2
3
4
5
6
7
8
9
(base) zhangwanyu@lih-workstation:~$ ls /lib/modules
5.15.0-124-generic 5.15.0-125-generic 5.15.0-127-generic 5.15.0-130-generic
(base) zhangwanyu@lih-workstation:~$ ls /lib/modules/*/kernel/drivers/video/nvidia*
/lib/modules/5.15.0-125-generic/kernel/drivers/video/nvidia-drm.ko /lib/modules/5.15.0-125-generic/kernel/drivers/video/nvidia-peermem.ko
/lib/modules/5.15.0-125-generic/kernel/drivers/video/nvidia.ko /lib/modules/5.15.0-125-generic/kernel/drivers/video/nvidia-uvm.ko
/lib/modules/5.15.0-125-generic/kernel/drivers/video/nvidia-modeset.ko
(base) zhangwanyu@lih-workstation:~$ uname -r
5.15.0-130-generic
(base) zhangwanyu@lih-workstation:~$

可以看到, 内核 5.15.0-125-generic 中有驱动。但是我们现在的内核版本( 使用 uname -r 查询)是 5.15.0-130-generic 。启动的内核错了,必然找不到GPU驱动。

(二)通过调整GRUB选项切换内核

要切换到上述内核,最直观的方法是启动过程中进入GRUB菜单选项:

image.png

但是我们希望的是能够让默认内核变成 5.15.0-125-generic ,这样我们每次启动就不需要手动设置了。

默认内核设置(GRUB选项)在 /etc/default/grub 中配置,其内容大致如下:

1
2
3
4
5
6
GRUB_DEFAULT=0
GRUB_TIMEOUT_STYLE=hidden
GRUB_TIMEOUT=0
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT=""
GRUB_CMDLINE_LINUX=""

其中最关键的一句是 GRUB_DEFAULT=0 ,其决定了GRUB选择哪一个内核进行启动。

关于这里的调整方法,我们参考了CSDN上的一个教程 《Ubuntu修改默认启动的内核版本(grub)》

首先,使用 grep -i "menuentry" /boot/grub/grub.cfg | cut -d "'" -f2 指令列出所有启动项:

image.png

从中选取包含 Linux 5.15.0-125-generic 字样的启动项内容,然后将其写入GRUB选项(依然在 /etc/default/grub 文件中):

1
2
#GRUB_DEFAULT=0 # 这一行给注释掉
GRUB_DEFAULT="Advanced options for Ubuntu>Ubuntu, with Linux 5.15.0-125-generic"

随后,运行 sudo update-grub 更新GRUB设置。

此后,重启计算机,则可以默认使用 Linux 5.15.0-125-generic 的内核。

重启,运行 nvidia-smi 指令,成功识别到显卡(如下图)。

1737626238714.png

完结撒花!