Linux内核的一些探索
这两天测试组里的服务器时踩的坑。浅浅记录一下探索过程,以防未来再度踩坑。
事情是这样的。最近这几天接到任务,需要帮忙测试组里的一台服务器,其间由于电源供电功率不足,我使用了一台主机的GPU供电线为另一台主机GPU进行了供电(这台主机原本有GPU)。测试结束后,我装回了原先的GPU,却发现这台机器启动后出现了显示器无输出、ssh无法连接的问题——由于显示器无输出,甚至我都不知道启动进行到了哪一步,遑论排查问题了。
这下可把我急坏了。好在,第二天在GPT老师和实验室师姐的帮助下,我们找到了重置主板BIOS设置的方法(即,扣除主板纽扣电池进行放电,并按照主机维护手册的说明,安装了主板CMOS复位跳线并短暂重启,之后移除CMOS复位跳线),总算解决了问题——终于在显示器上看见了输出,ssh登录也正常了。
然而问题接踵而来。虽然现在能进入系统,但是检测不到显卡驱动(在供电测试前,显卡驱动是正常的)。前面做测试时我们并没有动系统,按理说显卡驱动应该都还在呀,为什么突然检测不到了呢?这不太对劲。于是进行了一些更深入的挖掘,并终于了解到一些关于系统内核的知识点。
一、关于Linux启动顺序的一些知识点
Linux 启动过程大致如下:
- BIOS/UEFI 加载引导程序(GRUB):按下电源键时,计算机的 CPU 还无法直接运行操作系统,而是先执行主板上的 固件(Firmware),即 BIOS 或 UEFI。BIOS/UEFI 进行 POST(Power-On Self Test,自检),检测 CPU、内存、硬盘、显卡等是否正常。随后,查找 引导设备(通常是 SSD/HDD,或 U 盘/光盘),并从引导设备中读取引导扇区的内容——对Linux来说,引导扇区的内容通常是GRUB(GRand Unified Bootloader) ,这是 Linux 最常见的引导加载程序。
- **GRUB 选择内核并加载
initramfs
**:GRUB 显示启动菜单(如 Advanced options for Ubuntu),交由用户选择内核(或自动选择默认内核)。随后,GRUB将依次加载内核(vmlinuz-<version>
)和initramfs(initrd.img-<version>
),并将控制权交给 Linux 内核。 - 内核启动,并执行
initramfs
内部的init
脚本 : vmlinuz 是压缩后的内核,启动时会解压到内存中。内核必须能访问磁盘、USB、网络等设备,但此时文件系统未挂载,因此需要 initramfs 提供最小的运行环境。initramfs是一个临时的根文件系统,内部有一个 init 脚本,负责查找真正的根文件系统,并加载存储驱动。 initramfs
挂载真正的根文件系统(如/dev/sda2
):当initramfs完成根文件系统的查找和存储驱动加载以后,就通过switch_root
命令将控制权交给真正的文件系统。- 切换到根文件系统并启动系统服务: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
可以列出所有启动日志(其输出大致如下图)。如果某一次启动在这个日志中查询不到,一般可能的原因就是启动失败,直接连系统都没有进去。
(一)grub是干什么的
如下图:
(二)什么是initramfs
initramfs
(Initial RAM Filesystem,初始 RAM 文件系统)是 Linux 启动过程中用于引导的一个临时文件系统,它的主要作用是在系统启动时,为内核提供必要的驱动和工具,以便正确挂载根文件系统并启动 Linux 进程。
initramfs
主要在 内核引导阶段 发挥作用。一旦系统成功挂载真正的根文件系统(如 /dev/sda2
),initramfs
的任务就完成了,并且它会被 释放(discarded)。
虽然正常情况下 initramfs
会被丢弃,但在以下特殊情况下,它可能会 一直保持在 RAM 中:
- 根文件系统损坏或未找到(进入 “initramfs shell”)
- 例如
/etc/fstab
配置错误,导致系统无法挂载/
,你可能会进入initramfs
维护模式 ((initramfs) shell
)。
- 例如
- 系统采用了完全基于
initramfs
的rootfs
- 某些嵌入式 Linux 设备(如路由器、IoT 设备)可能会使用
initramfs
作为主要文件系统,而不再挂载磁盘上的根文件系统。
- 某些嵌入式 Linux 设备(如路由器、IoT 设备)可能会使用
dracut
的rd.break
选项- 在 RHEL/CentOS 里,可以通过
rd.break
让initramfs
进入调试模式,以便手动修复系统。
- 在 RHEL/CentOS 里,可以通过
二、查看和修改默认内核
(一)查看到系统中已存在的内核和模块
使用 ls -lhF /lib/modules
和 dpkg --list
指令,可以查看到系统中已存在的内核以及内核模块:
1 | (base) zhangwanyu@lih-workstation:~$ ls -lhF /lib/modules |
我们可以看到,这个系统里面有4个不同版本的内核。
可以使用 ls /lib/modules/*/kernel/drivers/video/nvidia*
查询在哪些内核当中有GPU相关驱动:
1 | (base) zhangwanyu@lih-workstation:~$ ls /lib/modules |
可以看到, 内核 5.15.0-125-generic
中有驱动。但是我们现在的内核版本( 使用 uname -r
查询)是 5.15.0-130-generic
。启动的内核错了,必然找不到GPU驱动。
(二)通过调整GRUB选项切换内核
要切换到上述内核,最直观的方法是启动过程中进入GRUB菜单选项:
但是我们希望的是能够让默认内核变成 5.15.0-125-generic
,这样我们每次启动就不需要手动设置了。
默认内核设置(GRUB选项)在 /etc/default/grub
中配置,其内容大致如下:
1 | GRUB_DEFAULT=0 |
其中最关键的一句是 GRUB_DEFAULT=0
,其决定了GRUB选择哪一个内核进行启动。
关于这里的调整方法,我们参考了CSDN上的一个教程 《Ubuntu修改默认启动的内核版本(grub)》 。
首先,使用 grep -i "menuentry" /boot/grub/grub.cfg | cut -d "'" -f2
指令列出所有启动项:
从中选取包含 Linux 5.15.0-125-generic
字样的启动项内容,然后将其写入GRUB选项(依然在 /etc/default/grub
文件中):
1 | #GRUB_DEFAULT=0 # 这一行给注释掉 |
随后,运行 sudo update-grub
更新GRUB设置。
此后,重启计算机,则可以默认使用 Linux 5.15.0-125-generic
的内核。
重启,运行 nvidia-smi
指令,成功识别到显卡(如下图)。
完结撒花!