对GRUB和initramfs的小探究

竞赛时对操作系统启动过程产生了些疑问,于是问题导向地浅浅探究了下GRUB和initramfs相关机制,相关笔记先放在这里了。

内核启动流程

在传统的BIOS系统中,计算机具体的启动流程如下:

  1. 电源启动:当计算机的电源打开时,电源供电给计算机的硬件设备。
  2. BIOS自检:计算机的BIOS固件会自检硬件设备,包括RAM、处理器、硬盘等,以确保它们正常工作。
  3. 引导设备选择:BIOS会根据预先定义的启动顺序(通常是硬盘、光驱、USB等)选择一个启动设备。
  4. MBR(Master Boot Record)加载:如果选择的启动设备是硬盘,BIOS会加载该硬盘的MBR,其中包含了引导加载程序。
  5. GRUB加载:MBR中的引导加载程序通常是GRUB(或其他引导加载程序)。GRUB会被加载到计算机的内存中,并开始执行。
  6. GRUB菜单:GRUB会显示一个菜单,列出可供选择的操作系统或内核。
  7. 操作系统加载:用户选择操作系统后,GRUB会加载相应的操作系统或内核,并将控制权交给它。

在本次内核编译配置过程中,最主要探究的是文件系统的装载过程,也即介于6-7之间的部分。

概述

文件系统在启动流程中的发展历程可以分为以下三个部分:

  1. GRUB文件系统

    由 GRUB 自身通过 BIOS 提供的服务加载

  2. initramfs

    由GRUB加载,用于挂载真正的文件系统

  3. 真正的根文件系统

下面,将介绍1和2两个流程。

GRUB

GRUB(GNU GRand Unified Bootloader)是一种常用的引导加载程序,用于在计算机启动时加载操作系统。

GRUB的主要功能是在计算机启动时提供一个菜单,让用户选择要启动的操作系统或内核。它支持多个操作系统,包括各种版本的Linux、Windows、BSD等。通过GRUB,用户可以在多个操作系统之间轻松切换。

除了操作系统选择,GRUB还提供了一些高级功能,例如引导参数的设置、内存检测、系统恢复等。它还支持在启动过程中加载内核模块和初始化RAM磁盘映像(initrd或initramfs)。

GRUB具有高度可配置性,允许用户自定义引导菜单、设置默认启动项、编辑内核参数等。它还支持引导加载程序间的链式引导,可以引导其他引导加载程序,如Windows的NTLDR。

GRUB的基本作用流程为:

  1. BIOS加载MBR,MBR加载GRUB,开始执行GRUB程序
  2. GRUB程序会读取grub.cfg配置文件
  3. GRUB程序依据配置文件,进行内核的加载、根文件系统的挂载等操作,最后将主导权转交给内核

grub.cfg

内核启动时,GRUB程序会读取/boot/grub/目录下的GRUB配置文件grub.cfg,其中记录了所有GRUB菜单可供选择的内核选项(menuentry)及其对应的启动依赖参数。以6.4.0内核选项为例:

# menuentry标识着GRUB菜单中的一个内核选项
menuentry 'Ubuntu' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-XXX' {recordfail # 记录上次启动是否失败,用于处理启动失败的情况load_video # 加载视频驱动模块,用于在启动过程中显示图形界面gfxmode $linux_gfx_mode # 设置图形模式insmod gzio # 加载gzio模块,提供对GZIP压缩和解压缩功能的支持# 如果是在Xen虚拟化平台上,则加载xzio和lzopio模块if [ x$grub_platform = xxen ]; then insmod xzio; insmod lzopio; fi insmod part_gpt # 加载part_gpt模块,支持GUID分区表(GPT)insmod ext2 # 加载ext2模块,支持ext2文件系统# 设置文件系统的根分区set root='hd0,gpt3' if [ x$feature_platform_search_hint = xy ]; thensearch --no-floppy --fs-uuid --set=root --hint-bios=hd0,gpt3 --hint-efi=hd0,gpt3 --hint-baremetal=ahci0,gpt3  XXXelsesearch --no-floppy --fs-uuid --set=root XXXfilinux   /boot/vmlinuz-6.4.0-rc3+ root=UUID=XXX ro text # 指定内核映像的路径和启动参数initrd  /boot/initrd.img-6.4.0-rc3+ # 指定initramfs映像的路径
}

可以看到,grub.cfg主要记录了一些该内核启动需要的依赖module,以及内核映像和initramfs映像的路径

menuentry的代码中,有以下几个要点值得注意:

  1. insmod gzio

    由于加载gzio模块,提供对GZIP压缩和解压缩功能的支持。

    看到这里我第一反应是觉得有点割裂,为啥这看着比较无关紧要的解压缩功能要在内核启动之前就需要有呢?于是我想起来在配置内核时,有一个选项是这样的:

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    在配置选项中,我们选择了对initramfs的支持,并且勾选了Support initial ramdisk/ramfs compressed using gzip ,也即在编译时通过gzip压缩initramfs的大小以节省空间。

    所以说,我们在内核启动之前,持有的initramfs处于被压缩的状态。故而,我们自然需要在内核启动之前安装gzio模块,从而支持之后对initramfs的解压缩了。

  2. insmod ext2

    这句代码说明,GRUB的临时文件系统为ext2类型,这句代码事实上是在安装GRUB建立临时文件的必要依赖包,从而GRUB程序之后才能建立其临时文件系统、从/boot/initrd.img获取initramfs映像。

  3. linux /boot/vmlinuz-6.4.0-rc3+ root=UUID=XXX ro text

    指定了启动参数,也即将根文件系统以只读(ro)的方式挂载在root=UUID=XXX对应的块设备上,并且默认以text方式(也即非图形化的Shell界面)启动内核。

    此处的启动参数可在下一个部分介绍的grub文件中个性化。

grub.cfg的生成与修改

实际运用中,很多时候需要对启动参数进行一些修改。下面介绍两种修改grub.cfg的方法。

/etc/default/grub

可以看到,grub.cfg其实格式较为固定(也即由一系列内容也比较相似的menuentry构成)。因而,实际上我们是通过grub.d生成grub.cfg的(6.S081实验中事实上也涉及了这一点),而/etc/default/grub则是GRUB程序以及grub.cfg生成的配置文件。下面介绍下该文件主要有哪些配置选项。

# If you change this file, run 'update-grub' afterwards to update
# /boot/grub/grub.cfg.
# For full documentation of the options in this file, see:
#   info -f grub -n 'Simple configuration'# 开机时GRUB界面的持续时间,此处设置为30s
GRUB_TIMEOUT=30
GRUB_CMDLINE_LINUX=""# 不使用图形化界面
#GRUB_TERMINAL=console
# 图形化界面的大小
#GRUB_GFXMODE=640x480
# 不使用UUID
#GRUB_DISABLE_LINUX_UUID=true# 隐藏recovery mode
#GRUB_DISABLE_RECOVERY="true"

重点看下这几个参数:

  1. GRUB_CMDLINE_LINUX

    表示最终生成的grub.cfg中的每一个menuentry中的linux那一行需要附加什么参数。

    例如说,如果设置为:

    # 表示initramfs在挂载真正的根文件系统之前,需要等待120s,用于防止磁盘没准备好导致的挂载失败
    GRUB_CMDLINE_LINUX="rootdelay=120" 
    

    那么,最终在menuentry中的启动参数就为:

    linux   /boot/vmlinuz-6.4.0-rc3+ root=UUID=XXX ro rootdelay=120 text
    

    其他一些常见的选项:

    # 直接以路径来标识块设备而非使用UUID。此为old option,建议尽量使用UUID
    GRUB_CMDLINE_LINUX="root=/dev/sda3"
    # 标明init进程(启动后第一个进程)的具体路径。此处指明为`/bin/sh`
    GRUB_CMDLINE_LINUX="init=/bin/sh"
    
  2. GRUB_DEFAULT

    参考 可以用来指定重启时的内核选项。如GRUB_DEFAULT="1> 0"表示选择第一个菜单界面的第2栏(Advanced for Ubuntu)和第二个菜单的第1个内核。

在修改完grub文件之后,我们需要执行sudo update-grub,来重新生成grub.cfg文件供下次启动使用。

在GRUB界面直接修改

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以在GRUB界面选中所需内核,按下e键:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

然后就可以对启动参数进行修改,^X退出。

值得注意的是,此修改仅对本次启动有效。如果需要长期修改,建议还是通过第一种方法去修改。

initramfs

GRUB程序会通过initrd.img启动initramfs,从而进行真正的根文件系统挂载。

initrd.img是一个Linux系统中的初始化内存盘(initial RAM disk)的映像文件。它是一个压缩的文件系统映像,通常在引导过程中加载到内存中,并提供了一种临时的根文件系统,以便在正式的根文件系统(通常位于硬盘上)可用之前提供必要的功能和模块。

我们可以通过unmkinitramfs /boot/initrd.img-6.4.0-rc3+ /tmp/initrd/命令解压initrd,探究里面到底有什么玩意。

├── bin -> usr/bin
├── conf
├── etc
├── init
├── lib -> usr/lib
├── lib32 -> usr/lib32
├── lib64 -> usr/lib64
├── libx32 -> usr/libx32
├── run
├── sbin -> usr/sbin
├── scripts
├── usr
└── var
init

可以看到,这实际上就是一个小型的文件系统,也即initramfs。它有自己的built-in Shell(BusyBox):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

有一些较少的Shell命令(bin和sbin目录下),以及用来挂载真正的根文件系统的代码逻辑(存储在scripts目录下)。【我猜】在正常情况下,系统会执行scripts下的脚本代码挂载真正的文件系统。当挂载出现异常时,系统就会将控制权交给initramfs内置的Shell BusyBox,由用户自己探究出了什么问题。

我们接下来可以追踪下initramfs的script目录下的文件系统挂载流程。

挂载真正文件系统的主要函数为local_mount_root

# 仅展示主要流程代码
local_mount_root()
{# 预处理,获取参数等(也即上面grub.cfg配置的root=UUID)local_topif [ -z "${ROOT}" ]; thenpanic "No root device specified. Boot arguments must include a root= parameter."fi# 根据UUID获取对应的块设备local_device_setup "${ROOT}" "root file system"ROOT="${DEV}"# 挂载前的预处理local_premount# 挂载mount ${roflag} ${FSTYPE:+-t "${FSTYPE}"} ${ROOTFLAGS} "${ROOT}" "${rootmnt?}"
}

由于研究这个是错误驱动(乐),因而我只主要看了下local_device_setup

# $1=device ID to mount设备ID
# $2=optionname (for root and etc)要挂载的是什么玩意,此处应为root file system
# $3=panic if device is missing (true or false, default: true)
# Sets $DEV to the resolved device node $DEV是最终获取到的块设备
local_device_setup()
{local dev_id="$1"local name="$2"local may_panic="${3:-true}"local real_devlocal time_elapsedlocal count# 获取grub.cfg的rootdelay参数的设备等待时间。如果没有该参数,默认是30秒local slumber=30if [ "${ROOTDELAY:-0}" -gt $slumber ]; thenslumber=$ROOTDELAYfi# 等待设备case "$dev_id" inUUID=*|LABEL=*|PARTUUID=*|/dev/*)FSTYPE=$( wait-for-root "$dev_id" "$slumber" );;*)wait_for_udev 10;;esac# 等待结束了。如果条件为真,说明还是获取不到对应的设备,那就只能说明这个设备死了# 所以我们就得把问题告诉用户,让用户自己解决,并且进入BusyBox Shell# We've given up, but we'll let the user fix matters if they canwhile ! real_dev=$(resolve_device "${dev_id}") ||! get_fstype "${real_dev}" >/dev/null; doif ! $may_panic; thenecho "Gave up waiting for ${name}"return 1fiecho "Gave up waiting for ${name} device.  Common problems:"echo " - Boot args (cat /proc/cmdline)"echo "   - Check rootdelay= (did the system wait long enough?)"if [ "${name}" = root ]; thenecho "   - Check root= (did the system wait for the right device?)"fiecho " - Missing modules (cat /proc/modules; ls /dev)"panic "ALERT!  ${dev_id} does not exist.  Dropping to a shell!"doneDEV="${real_dev}"
}

可以看到,这里如果进入错误状态,最终就是这样的效果2333:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/117726.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

『heqingchun-Qt的艺术-优雅界面设计开发』

Qt的艺术-优雅界面设计开发 效果图 一、新建Qt窗口工程 二、准备资源文件 1.图标资源 链接: 图标资源 2.Qss资源 链接: Qss资源 三、设计开发 项目源码链接: CSDN资源

JS中面向对象的程序设计

面向对象(Object-Oriented,OO)的语言有一个标志,那就是它们都有类的概念,而通过类可以创建任意多个具有相同属性和方法的对象。但在ECMAScript 中没有类的概念,因此它的对象也与基于类的语言中的对象有所不…

基于大数据的社交平台数据爬虫舆情分析可视化系统 计算机竞赛

文章目录 0 前言1 课题背景2 实现效果**实现功能****可视化统计****web模块界面展示**3 LDA模型 4 情感分析方法**预处理**特征提取特征选择分类器选择实验 5 部分核心代码6 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 基于大数据…

Go语言入门心法(十五):Go微服务实战

Go语言入门心法(一): 基础语法 Go语言入门心法(二): 结构体 Go语言入门心法(三): 接口 Go语言入门心法(四): 异常体系 Go语言入门心法(五): 函数 Go语言入门心法(六): HTTP面向客户端|服务端编程 Go语言入门心法(七): 并发与通道 Go语言入门心法(八): mysql驱动安装报错o…

Redis主从模式(二)---拓扑结构及复制过程

目录 一, Redis主从模式下的复制拓扑结构 1.1 一主一从结构 1.2 一主多从结构 1.3 树形主从结构 二, 主从复制过程 2.1 主从复制建立复制流程图 2.2 数据同步(psyc) 1.replicationid/replid (复制id) 2.offset(偏移量) 2.3 psync运行流程 2.4 全量复制 2.5 部分复制…

操作教程|如何注册成为Moonbeam社区代表参与治理

社区代表是高度参与社区治理的社区成员,其主要职责是将社区成员委托给他们的投票权参与社区投票,并确保链上治理稳健发展和活跃参与度。本文将向您展示如何快速注册成为社区代表。 首先,前往Moonbeam委托网站,点击网页右上角的“…

老师们都在用的办公好物

现在还有老师不知道班级查询系统吗?各位老师们,向大家推荐一款超级实用的班级查询系统,帮你轻松管理学生信息,省去繁琐的手动操作,还能让学生们自主查询,简直是老师的福音! 如果你在编程方面感到有些吃力&…

【vue3】依赖注 provide、inject(父组件与儿子、孙子、曾孙子组件之间的传值)

一、基本用法&#xff1a; //父组件 import { ref, provide } from vue const radio ref<string>(red) provide(myColor,radio) //注入依赖//儿子组件、孙子组件、曾孙子组件 import { inject } from vue import type { Ref } from vue; const myColor inject<Ref&l…

CICD 流程学习(五)Jenkins后端工程构建

案例1&#xff1a;数据库服务部署 MySQL部署 #安装MySQL服务 [rootServices ~]# yum clean all; yum repolist -v ... Total packages: 8,265 [rootServices ~]# yum -y install mysql.x86_64 mysql-server.x86_64 mysql-devel.x86_64 ... Complete! [rootServices ~]# #启动…

SpringCloud复习:(3)LoadBalancerInterceptor

使用Ribbon时&#xff0c;execute方法会由RibbonLoadBalancerClient类来实现 它会调用重载的execute方法 getLoadBalancer默认会返回ZoneAwareLoadBalancer&#xff08;基类是BaseLoadBalancer).此处调用的getServer方法就会根据负载均衡策略选择适当的服务器来为下一步的htt…

计算机网络第3章-运输层(2)

可靠数据传输原理 可靠数据传输依靠数据在一条可靠信道上进行传输。 TCP也正是依靠可靠信道进行传数据&#xff0c;从而数据不会被丢失。 而实现这种可靠数据传输服务是可靠数据传输协议的责任 构造可靠数据传输协议 1.经完全可靠信道的可靠数据传输&#xff1a;rdt1.0 在…

使用性能监控软件的主要措施

性能监控软件是管理和维护计算机系统的不可或缺的工具。它们提供了实时性能数据&#xff0c;帮助用户优化资源利用、提高系统稳定性和提供更好的用户体验。选择合适的性能监控软件并正确使用它们可以帮助你在竞争激烈的数字时代脱颖而出&#xff0c;确保你的系统始终在最佳状态…

51单片机练习(04)

eg1:使用定时器的方式实现单片机流水灯 #include <REGX52.H> #include <INTRINS.H> #define uchar unsigned char #define uint unsigned int uchar temp,t0;// 初始化函数 void init(){temp 0xfe;//第一个发光二级管点亮P1 temp;// 初始化定时器TMOD 0x11;TH0…

抽丝剥茧,Redis使用事件总线EventBus或AOP优化健康检测

目录 前言 Lettuce 什么是事件总线EventBus&#xff1f; Connected Connection activated Disconnected Connection deactivated Reconnect failed 使用 一种另类方法—AOP 具体实现 前言 在上一篇深入浅出&#xff0c;SpringBoot整合Quartz实现定时任务与Redis健康…

openGauss学习笔记-108 openGauss 数据库管理-管理用户及权限-用户

文章目录 openGauss学习笔记-108 openGauss 数据库管理-管理用户及权限-用户108.1 创建、修改和删除用户108.2 私有用户108.3 永久用户108.4 用户认证优先规则 openGauss学习笔记-108 openGauss 数据库管理-管理用户及权限-用户 使用CREATE USER和ALTER USER可以创建和管理数据…

百度文心一言4.0——使用及API测试

登录百度智能云&#xff1a;百度智能云 文心一言4.0使用 开通付费&#xff1a; 创建应用&#xff1a; 自行创建应用名称&#xff1a; 对话测试&#xff1a; API测试 ERNIE-Bot-4 API&#xff1a;ERNIE-Bot-4 打开链接查看自己的API Key&#xff0c;Secret Key。 可参…

高速下载b站视频的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

JavaWeb——Servlet原理、生命周期、IDEA中实现一个Servlet(全过程)

6、servlet 6.1、什么是servlet 在JavaWeb中&#xff0c;Servlet是基于Java编写的服务器端组件&#xff0c;用于处理客户端&#xff08;通常是Web浏览器&#xff09;发送的HTTP请求并生成相应的HTTP响应。Servlet运行在Web服务器上&#xff0c;与Web容器&#xff08;如Tomcat&…

如何平衡 Wi-Fi 7 与未来 5G/6G 的发展?

随着社会进步&#xff0c;人们对信息技术的需求不断提升。当前互联网、大数据、云计算、人工智能、区块链等新技术深刻演变&#xff0c;产业数字化、智能化、绿色化转型不断加速&#xff0c;智能产业、数字经济蓬勃发展&#xff0c;极大改变全球要素资源配置方式、产业发展模式…

Auth.js:多合一身份验证解决方案 | 开源日报 No.60

nodejs/node Stars: 96.2k License: NOASSERTION Node.js 是一个开源的、跨平台的 JavaScript 运行时环境。它具有以下关键特性和核心优势&#xff1a; 强大&#xff1a;Node.js 提供了强大且高效的服务器端运行能力&#xff0c;可以处理并发请求&#xff0c;并支持异步编程…