Linux 之旅 19:启动流程、模块管理与loader_default.target-程序员宅基地

技术标签: 运维  bootloader  linux  grub  boot  

Linux 之旅 19:启动流程、模块管理与loader

image-20210907201413018

图源:pexels

Linux启动流程分析

启动流程一览

通常操作系统的启动过程可以分为以下几个步骤:

  1. 加载BIOS以获取硬件信息并进行自检,这样就可以获取到第一个可启动设备。
  2. 读取并执行第一个可启动设备的MBR内的启动引导程序(grub2spfdisk等)。
  3. 根据启动引导程序的设置加载Kernel,Kernel进行硬件检测并加载需要的驱动和模块。
  4. Kernel启动Systemd进程,并且用default.target模式准备操作系统环境:
    1. systemd执行sysinit.target初始化系统
    2. systemd执行basic.target准备基础的操作系统环境
    3. systemd启动multi-user.target下的本机与服务器服务
    4. systemd执行multi-user.target下的/etc/rc.d/rc.local文件
    5. systemd执行multi-user.target下的getty.target及登录服务
    6. systemd执行graphical所需的服务

BIOS、boot loader与Kernel加载

BIOS、启动自我检测与MBR/GPT

操作系统启动后第一件事情就是加载BIOS(Basic Input Output System),并通过BIOS加载CMOS信息,由CMOS内设置的信息读取主机的各种硬件信息。

关于BIOS和CMOS的详细信息可以阅读BIOS和CMOS是什么?教大家认识BIOS和CMOS!

从CMOS中可以获取到第一个可用的启动设备(就是我们出重装系统的时候会在BIOS中修改的那个启动顺序列表),然后会从第一个启动设备的MBR分区中获取 boot loader(启动引导程序),如果是多系统,还可能由MBR中的boot loader加载其它分区中的boot loader,再启动具体的操作系统。

boot loader

如果一个磁盘上只装了一个操作系统,其boot loader就在磁盘的MBR中,也就是整个磁盘的第一块扇区中。如果是多系统,每个系统的boot loader会保存在系统所在的分区的第一块扇区(启动扇区,boot sector)中,而MBR中的boot loader仅起一个转交启动工作的用途,会在用户选择启动哪个系统之后,将系统启动工作转移给对应系统的boot loader来执行。整个过程可以用下图表示:

img

图源:鸟哥的私房菜

所以boot loader具有以下功能:

  • 提供菜单:用户可以选择启动哪一个选项启动(对于单系统,也可以提供救援模式之类的启动项)
  • 加载内核文件:直接加载内核的相关文件到内存
  • 转交启动工作给其它loader:如果是多系统,MBR中的boot loader会将具体的启动工作转交给对应的操作系统的boot loader。

不同的操作系统因为使用的文件系统不同,比如Linux使用xfs而Windows使用fat,所以使用的boot loader也会有所不同,而Windows的boot loader行为比较简单,不具备转交启动工作给非Windows系统的boot loader的功能,而且安装Windows时不会提供选项,必定会将boot loader写入MBR中。这样就产生了一个结果,只要安装了Windows,无论其它分区有没有安装别的操作系统,都无法再由boot loader启动,只能启动Windows。所以如果要安装多系统,比如Windows和Linux,最好先安装Windows,再安装Linux。使用Linux的boot loader来提供多个操作系统的加载选项即可。

加载内核检测硬件与initramfs的功能

Linux内核的相关文件放在/boot中,并且一般会保存在/boot/vmlinuz

[icexmoon@xyz ~]$  ls --format=single-column -F /boot
config-3.10.0-1160.el7.x86_64 # 编译内核时的相关配置
efi/ 
grub/
grub2/ # boot loader grub2的相关文件
initramfs-0-rescue-f5a85e92d51e4d40975fd956fd775f9c.img # 救援时使用的虚拟文件系统
initramfs-3.10.0-1160.el7.x86_64.img # 一般开机会使用的虚拟文件系统
symvers-3.10.0-1160.el7.x86_64.gz
System.map-3.10.0-1160.el7.x86_64
testing.img
vmlinuz-0-rescue-f5a85e92d51e4d40975fd956fd775f9c* # 救援用的内核文件
vmlinuz-3.10.0-1160.el7.x86_64* # 内核文件

内核文件被加载以后,仅能直接使用内核中的相关功能,内核中没有的功能就要以“内核模块”的方式加载,这些模块保存在/lib/modules目录中。

要直接给对内核中的功能增加或修改相当麻烦,需要重新编译内核,所以一般来说,能做成“内核模块”的功能都会做成模块,这样无论是增加还是修改就会简单的多,只要内核挂载相应的模块即可,无需重新编译内核。但是这样会有一个新的问题,即内核模块所在的目录/lib/modules是和根目录/在一个分区的,而Linux加载内核后仅会挂载内核所在的这个分区,更要命的是,xfs之类的当前主要使用的文件系统驱动是以模块的方式提供的,并非直接编译在内核中,而内核需要先加载使用xfs文件系统的根目录分区才能读取到模块,这就变成了一个鸡生蛋蛋生鸡的问题。

万幸的是Linux提供了一个叫做虚拟文件系统(Initial RAM Disk 或 Initial RAM Filesystem)的东西,这是一个保存在/boot中的压缩文件,其中包含了一些内核必须的模块,比如文件系统或者磁盘驱动等。内核会先解压这个虚拟文件系统到内存中,并用它作为根目录,从而加载必须的模块,然后就可以加载“真正的”根目录所在的文件系统了,加载后会取代虚拟文件系统。

下面实际解压系统中的虚拟文件系统查看其组成,因为CentOS 7的虚拟文件系统压缩包格式比较特殊,所以这里参考centos7 initramfs解包 打包编写了一个解包的脚本:

initramfs_file=$(ls -a /boot/initramfs-$(uname -r).img)
tmp_home='/tmp/initramfs'
if [ ! -d $tmp_home ]
then
        mkdir $tmp_home
fi
initramfs_tmp="${tmp_home}/initramfs_file"
if [ ! -f $initramfs_tmp ]
then
        cp $initramfs_file ${initramfs_tmp}
fi
/usr/lib/dracut/skipcpio $initramfs_tmp | zcat | cpio -id

运行这个脚本解包即可:

[root@xyz initramfs]# sh unzip_initramfs.sh
128811[root@xyz initramfs]# ll
总用量 31352
lrwxrwxrwx.  1 root root        7 97 22:00 bin -> usr/bin
drwxr-xr-x.  2 root root       45 97 22:00 dev
drwxr-xr-x. 12 root root     4096 97 22:00 etc
lrwxrwxrwx.  1 root root       23 97 22:00 init -> usr/lib/systemd/systemd
-rw-------.  1 root root 32089078 97 22:00 initramfs_file
lrwxrwxrwx.  1 root root        7 97 22:00 lib -> usr/lib
lrwxrwxrwx.  1 root root        9 97 22:00 lib64 -> usr/lib64
drwxr-xr-x.  2 root root        6 97 22:00 proc
drwxr-xr-x.  2 root root        6 97 22:00 root
drwxr-xr-x.  2 root root        6 97 22:00 run
lrwxrwxrwx.  1 root root        8 97 22:00 sbin -> usr/sbin
-rwxr-xr-x.  1 root root     3117 97 22:00 shutdown
drwxr-xr-x.  2 root root        6 97 22:00 sys
drwxr-xr-x.  2 root root        6 97 22:00 sysroot
drwxr-xr-x.  2 root root        6 97 22:00 tmp
-rw-r--r--.  1 root root      302 97 21:59 unzip_initramfs.sh
drwxr-xr-x.  7 root root       66 97 22:00 usr
drwxr-xr-x.  2 root root       29 97 22:00 var

systemd

systemd是系统内核启动后开启的第一个服务进程,其功能是准备一个基础的操作系统运行环境,包括主机名、网络设置、语言设置、文件系统格式等,所有的这些都是通过一个默认的启动服务集合default.target实现的。

target与runlevel

之前我们说过,现在的服务管理机制systemd与以前的system V是有所不同的,以前用于区分系统模式的是runlevel,而现在则使用的是target,为了兼容性方面的考虑,systemd将部分以前的runlevel映射到了某些target上:

[icexmoon@xyz ~]$ ll -d /usr/lib/systemd/system/runlevel*.target | cut -c 41-
/usr/lib/systemd/system/runlevel0.target -> poweroff.target
/usr/lib/systemd/system/runlevel1.target -> rescue.target
/usr/lib/systemd/system/runlevel2.target -> multi-user.target
/usr/lib/systemd/system/runlevel3.target -> multi-user.target
/usr/lib/systemd/system/runlevel4.target -> multi-user.target
/usr/lib/systemd/system/runlevel5.target -> graphical.target
/usr/lib/systemd/system/runlevel6.target -> reboot.target

因此,以前用于操作系统模式转换的命令init x是依然可以使用的,其和systemctl命令的对应关系是:

SystemV systemd
init 0 systemctl poweroff
init 1 systemctl rescue
init [234] systemctl isolate multi-user.target
init 5 systemctl isolate graphical.target
init 6 systemctl reboot
systemd的处理流程

systemd会通过启动default.target的方式准备一个基本的操作系统环境,从Linux 之旅 17:系统服务(daemons)中我们已经知道,default.target可以有两个选项:graphical.targetmulti-user.target,实际上default.target就是一个到这两者之一的一个alias

比较常用的是graphical.target,并且其本身包含multi-user.target,所以这里假设当前Linux主机使用的default.targetgraphical.target

我们来看graphical.target具体会干些什么:

[root@xyz ~]# cat /usr/lib/systemd/system/graphical.target | grep -v '^#'

[Unit]
Description=Graphical Interface
Documentation=man:systemd.special(7)
Requires=multi-user.target
Wants=display-manager.service
Conflicts=rescue.service rescue.target
After=multi-user.target rescue.service rescue.target display-manager.service
AllowIsolate=yes

比较重要的设置是RequiresWants,前者是说启动graphical.target之前要先启动哪些服务,后者是说启动graphical.target之后要启动哪些服务。

从这里可以看出,相关服务的启动顺序是multi-user.target->graphical.target->display-manager.service。当然,前两个是target,是服务集,最后一个是单纯的服务。

现在我们看multi-user.target会干些什么:

[root@xyz ~]# cat /usr/lib/systemd/system/multi-user.target | grep -v '^#'

[Unit]
Description=Multi-User System
Documentation=man:systemd.special(7)
Requires=basic.target
Conflicts=rescue.service rescue.target
After=basic.target rescue.service rescue.target
AllowIsolate=yes

可以看到,要启动multi-user.target就要先启动basic.target

最后再看一下默认安装的需要被multi-user.target加载的服务有哪些:

[root@xyz ~]# ls /usr/lib/systemd/system/multi-user.target.wants/
dbus.service  plymouth-quit.service       systemd-ask-password-wall.path  systemd-update-utmp-runlevel.service
getty.target  plymouth-quit-wait.service  systemd-logind.service          systemd-user-sessions.service

用户自定义的需要被multi-user.target加载的服务有:

[root@xyz ~]# ls /etc/systemd/system/multi-user.target.wants/
abrt-ccpp.service    crond.service                          libstoragemgmt.service  postfix.service         sshd.service
abrtd.service        firewalld.service                      libvirtd.service        remote-fs.target        sysstat.service
abrt-oops.service    initial-setup-reconfiguration.service  mcelog.service          rhel-configure.service  tuned.service
abrt-vmcore.service  irqbalance.service                     mdmonitor.service       rngd.service            vdo.service
abrt-xorg.service    kdump.service                          ModemManager.service    rpcbind.service         vmtoolsd.service
atd.service          ksm.service                            NetworkManager.service  rsyslog.service         vsftpd2.service
auditd.service       ksmtuned.service                       nfs-client.target       smartd.service          vsftpd.service

事实上,如果设置了开机自启动的服务,就会以软连接的形式添加在这里,反之就会被从这里删除。

整体上,systemd的启动流程是这样的:

  1. 启动local-fs.targetswap.targete:这两个target主要用于挂载本机/etc/fstab中的文件系统和交换分区。
  2. 启动sysinit.target:这个target用于检测硬件,并加载所需要的内核模块等。
  3. 启动basic.target:这个target用于设置基础的操作系统环境,包括加载外围的硬件驱动和防火墙等。
  4. 启动multi-user.target:这个target用于其他一般系统或网络服务的加载。
  5. 启动图形界面相关的服务:如gdm.service等。

sysinit.target

sysinit.target的主要工作包含以下部分:

  • 特殊文件系统设备的挂载:包括dev-hugepages.mountdev-mqueue.mount等挂载服务,这些服务主要用于内存分页或者消息队列等功能。
  • 特殊文件系统的启用:包括磁盘阵列、网络驱动器(iscsi)、LVM文件系统、文件系统对照服务(multipath)等。
  • 启动过程的信息传递与动画执行:使用plymouthd服务搭配plymouth命令来传递动画与信息。
  • 日志式日志文件的使用:就是systemd-journald这个服务的启用。
  • 加载额外的内核模块:通过/etc/modules-load.d/*.conf文件的设置,让内核额外加载管理员所需的内核模块。
  • 加载额外的内核参数设置:包括/etc/sysctl.conf以及/etc/sysctl.d/*.conf内的设置。
  • 启动服务的随机数生成器:随机数生成器可以帮助系统进行一些密码加密运算的功能。
  • 设置终端(console)字体。
  • 启动动态设备管理器:udevd

主要是基本的内核功能、文件系统、文件系统该设备的驱动等。

basic.target

basic.target主要包含的功能有:

  • 加载alsa音效驱动
  • 加载firewalld防火墙:CentOS 7.x使用firewalld替换了iptables
  • 加载CPU的微指令
  • 启动与设置SELinux的安全上下文。
  • 将目前启动过程中所产生的信息写入/var/log/dmesg中。
  • /etc/sysconfig/modules/*.modules/etc/rc.modules中加载管理员指定的模块。
  • 加载systemd支持的timer功能。

basic.target启动完毕后,你就拥有一个基本的操作系统了,只差一些登录服务、网络服务、认证服务等service。

multi-user.target

实际上,Linux主机上运行的大部分daemon的开机启动都是依附于multi-user.target的,这点可以通过以下测试证实:

[root@xyz ~]# systemctl disable vsftpd.service
Removed symlink /etc/systemd/system/multi-user.target.wants/vsftpd.service.
[root@xyz ~]# systemctl enable vsftpd.service
Created symlink from /etc/systemd/system/multi-user.target.wants/vsftpd.service to /etc/systemd/system/vsftpd.service.

可以看到,都是通过在multi-user.target.wants目录下增加或者删除链接的方式实现的。

兼容SystemV的rc-local.service

过去的SystemV中,开启自启动程序是直接修改/etc/rc.d/rc.local这个文件实现的,这和新的systemd实现方式不同。

为了兼容SystemV的程序自启动方式,systemd中存在一个rc-local.service服务,这个服务可以承担执行rc.local文件中设置的自启动程序的工作。并且最妙的是这个服务虽然默认没有被启动,但其启动方式是static,即依赖于某些事件的结果来决定是否开机启动:

[root@xyz ~]# systemctl status rc-local.service
● rc-local.service - /etc/rc.d/rc.local Compatibility
   Loaded: loaded (/usr/lib/systemd/system/rc-local.service; static; vendor preset: disabled)
   Active: inactive (dead)

事实上rc-local.service会检测rc.local这个文件,如果这个文件具有执行权限,就会开机自动启动:

[root@xyz ~]# ll /etc/rc.d/rc.local
-rw-r--r--. 1 root root 473 102 2020 /etc/rc.d/rc.local
[root@xyz ~]# chmod a+x /etc/rc.d/rc.local
[root@xyz ~]# systemctl list-dependencies multi-user.target | grep rc-local
● ├─rc-local.service

graphical.target

graphical.target很好理解,就是相对于multi-user.target多出来图形相关的服务和程序,如window display manager(WDM,图形界面管理程序)等。

如果要详细了解,可以通过查看其依赖关系:

[root@xyz ~]# systemctl list-dependencies graphical.target | head
graphical.target
● ├─accounts-daemon.service
● ├─gdm.service
● ├─initial-setup-reconfiguration.service
● ├─network.service
● ├─rtkit-daemon.service
● ├─systemd-readahead-collect.service
● ├─systemd-readahead-replay.service
● ├─systemd-update-utmp-runlevel.service
● ├─udisks2.service

其中比较重要的有gdm.service,功能是让用户可以利用图形界面进行登录。

启动过程中会用到的主要配置文件

关于内核需要加载的额外模块,有两个地方的配置比较重要:

  • /etc/modules-load.d/*.conf:决定内核要加载哪些模块
  • /etc/modprobe.d/*.conf:决定加载模块时使用哪些额外参数

比如因为之前我们安装的FTP服务vsftpd,就可能需要添加额外的防火墙模块nf_conntrack_ftp

[root@xyz ~]# ll /etc/modules-load.d/
总用量 0
[root@xyz ~]# vim /etc/modules-load.d/vsftpd.conf
[root@xyz ~]# cat /etc/modules-load.d/vsftpd.conf
nf_conntrack_ftp

因为我们有给vsftpd服务添加一个特别的端口555,所以需要在加载该模块时指定额外参数:

[root@xyz ~]# vim /etc/modprobe.d/vsftpd.conf
[root@xyz ~]# cat /etc/modprobe.d/vsftpd.conf
options nf_conntrack_ftp ports=555

最后测试一下:

[root@xyz ~]# lsmod | grep nf_conntrack_ftp
[root@xyz ~]# systemctl restart systemd-modules-load.service
[root@xyz ~]# lsmod | grep nf_conntrack_ftp
nf_conntrack_ftp       18478  0
nf_conntrack          139264  8 nf_nat,nf_nat_ipv4,nf_nat_ipv6,xt_conntrack,nf_nat_masquerade_ipv4,nf_conntrack_ftp,nf_conntrack_ipv4,nf_conntrack_ipv6

内核与内核模块

Linux中,和内核相关的文件或目录有:

  • /boot/vmlinuz/boot/vmlinuz-version:Linux内核文件。
  • /boot/initramfs/boot/initramfs-version:打包后的虚拟文件系统。
  • /lib/modules/version/kernel/lib/modules/$(uname -r)/kernel:内核模块。
  • /usr/src/linux/usr/src/kernels:内核源码(默认不安装)
  • /proc/version:内核版本
  • /proc/sys/kernel:系统内核功能

内核模块与依赖性

内核模块之间是有依赖关系的,为了更好地管理这些关系,Linux中由/lib/modules/$(uname -r)/modules.dep这个文件保存内核模块之间的依赖关系。

这个文件是不需要我们手动维护的,只需要使用depmod命令就可以自动检查模块依赖关系并生成该文件:

[root@study ~]# cp a.ko /lib/modules/$(uname -r)/kernel/drivers/net
[root@study ~]# depmod

因为目前也没有学习模块编译的内容,也不能随意加载模块(可能导致严重后果),所以这里我没有实践,直接摘抄了《鸟哥的Linux私房菜》中的示例。

查看内核模块

使用lsmod可以查看内核中已经加载的模块:

[root@xyz ~]# lsmod | head
Module                  Size  Used by
nf_conntrack_ftp       18478  0
xt_CHECKSUM            12549  1
ipt_MASQUERADE         12678  3
nf_nat_masquerade_ipv4    13463  1 ipt_MASQUERADE
tun                    36164  1
devlink                60067  0
ip6t_rpfilter          12595  1
ip6t_REJECT            12625  2
nf_reject_ipv6         13717  1 ip6t_REJECT

输出的信息有三个字段:

  • Module:模块名称
  • Size:模块大小
  • Used by:模块的依赖关系(被其它哪些模块使用)

如果要查看模块的具体信息:

[root@xyz ~]# modinfo tun
filename:       /lib/modules/3.10.0-1160.el7.x86_64/kernel/drivers/net/tun.ko.xz
alias:          devname:net/tun
alias:          char-major-10-200
license:        GPL
author:         (C) 1999-2004 Max Krasnyansky <[email protected]>
description:    Universal TUN/TAP device driver
retpoline:      Y
rhelversion:    7.9
srcversion:     E26A36A927427B2BAE3FB17
depends:
intree:         Y
vermagic:       3.10.0-1160.el7.x86_64 SMP mod_unload modversions
signer:         CentOS Linux kernel signing key
sig_key:        E1:FD:B0:E2:A7:E8:61:A1:D1:CA:80:A2:3D:CF:0D:BA:3A:A4:AD:F5
sig_hashalgo:   sha256

除了像上面那样可以使用modinfo查看加载的内核模块的具体信息,还可以直接查看未加载的模块文件,比如modinfo a.ko

内核模块的加载与删除

可以使用insmod手动加载内核模块:

[root@study ~]# insmod /lib/modules/$(uname -r)/kernel/fs/fat/fat.ko
[root@study ~]# lsmod | grep fat
fat                    65913  0

要移除模块,可以:

[root@study ~]# rmmod fat
[root@study ~]# insmod /lib/modules/$(uname -r)/kernel/fs/fat/vfat.ko.xz
insmod: ERROR: could not insert module /lib/modules/3.10.0-229.el7.x86_64/kernel/fs/fat/
 vfat.ko.xz: Unknown symbol in module

这里我同样没有实践,直接使用《鸟哥的私房菜》中的示例。

可以看到,因为模块是有依赖性的,所以在加载模块的时候可能因为缺乏所依赖的模块而失败,这时候就可以使用modprobe命令加载模块,这个命令会检查modules.dep文件中的模块依赖关系,然后先加载所依赖的模块,再加载指定的模块:

[root@xyz ~]# modprobe vfat
[root@xyz ~]# lsmod | grep vfat
vfat                   17461  0
fat                    65950  1 vfat
[root@xyz ~]# modprobe -r vfat
[root@xyz ~]# lsmod | grep vfat

modprove -r可以删除模块。

Boot Loader:Grub2

boot loader

boot loader包括两个部分:程序本体和配置文件。程序本体是保存在MBR中的,但因为MBR中的空间有限,所以配置文件是单独存放的,grub2的配置文件保存在/boot/grub2中:

[root@xyz ~]# ls -l /boot/grub2
总用量 32
-rw-r--r--. 1 root root   84 724 14:45 device.map
drwxr-xr-x. 2 root root   25 724 14:45 fonts # 字体文件
-rw-r--r--. 1 root root 4309 724 14:47 grub.cfg # grub2的主配置文件
-rw-r--r--. 1 root root 1024 724 14:47 grubenv
drwxr-xr-x. 2 root root 8192 724 14:45 i386-pc # x86计算机所需的grub2模块
drwxr-xr-x. 2 root root 4096 724 14:45 locale # 语言和编码
[root@xyz ~]# ls -l /boot/grub2/i386-pc/
总用量 2388
-rw-r--r--. 1 root root   9936 724 14:45 acpi.mod
-rw-r--r--. 1 root root   1316 724 14:45 adler32.mod
-rw-r--r--. 1 root root   5696 724 14:45 affs.mod
-rw-r--r--. 1 root root   6632 724 14:45 afs.mod
-rw-r--r--. 1 root root  15764 724 14:45 ahci.mod
-rw-r--r--. 1 root root    492 724 14:45 all_video.mod
-rw-r--r--. 1 root root   1028 724 14:45 aout.mod
-rw-r--r--. 1 root root   2932 724 14:45 archelp.mod
-rw-r--r--. 1 root root   5588 724 14:45 ata.mod
-rw-r--r--. 1 root root   4212 724 14:45 at_keyboard.mod
-rw-r--r--. 1 root root   1628 724 14:45 backtrace.mod
-rw-r--r--. 1 root root   7212 724 14:45 bfs.mod
-rw-r--r--. 1 root root   4556 724 14:45 biosdisk.mod
-rw-r--r--. 1 root root   2336 724 14:45 bitmap.mod
-rw-r--r--. 1 root root   3636 724 14:45 bitmap_scale.mod
-rw-r--r--. 1 root root   2160 724 14:45 blocklist.mod
...省略

grub.cfg

grub2的优点有:

  • 可以识别和支持多种文件系统
  • 启动时可以自行编辑和修改启动设置选项
  • 可以动态查找配置文件,即修改grub2的配置文件后不需要重新编译和安装grub2,重启后会自动加载
磁盘与分区在grub2中的代号

grub2中定位磁盘和分区的代号与Linux不同,Linux使用sda1这样的编号,而grub2使用的代号是:


(hd0,1)         # 一般的預設語法,由 grub2 自動判斷分割格式
(hd0,msdos1)    # 此磁碟的分割為傳統的 MBR 模式
(hd0,gpt1)      # 此磁碟的分割為 GPT 模式

需要注意的是,磁盘的编号是从0开始的,比如第一块磁盘是hd0,第二块磁盘是hd1,以此类推。而磁盘上的分区编号则是从1开始,比如第一块磁盘的第一个分区是(hd0,1),第二个分区是(hd0,2),以此类推。

grub.cfg是grub2的主配置文件:

[root@xyz ~]# cat /boot/grub2/grub.cfg | grep -v '^#' | grep -v '^$'
set pager=1
if [ -s $prefix/grubenv ]; then
  load_env
fi
if [ "${next_entry}" ] ; then
   set default="${next_entry}"
   set next_entry=
   save_env next_entry
   set boot_once=true
else
   set default="${saved_entry}"
fi
if [ x"${feature_menuentry_id}" = xy ]; then
  menuentry_id_option="--id"
else
  menuentry_id_option=""
fi
export menuentry_id_option
if [ "${prev_saved_entry}" ]; then
  set saved_entry="${prev_saved_entry}"
  save_env saved_entry
  set prev_saved_entry=
  save_env prev_saved_entry
  set boot_once=true
fi
function savedefault {
    
  if [ -z "${boot_once}" ]; then
    saved_entry="${chosen}"
    save_env saved_entry
  fi
}
function load_video {
    
  if [ x$feature_all_video_module = xy ]; then
    insmod all_video
  else
    insmod efi_gop
    insmod efi_uga
    insmod ieee1275_fb
    insmod vbe
    insmod vga
    insmod video_bochs
    insmod video_cirrus
  fi
}
terminal_output console
if [ x$feature_timeout_style = xy ] ; then
  set timeout_style=menu
  set timeout=5
else
  set timeout=5
fi
set tuned_params=""
set tuned_initrd=""
if [ -f ${prefix}/user.cfg ]; then
  source ${prefix}/user.cfg
  if [ -n "${GRUB2_PASSWORD}" ]; then
    set superusers="root"
    export superusers
    password_pbkdf2 root ${GRUB2_PASSWORD}
  fi
fi
menuentry 'CentOS Linux (3.10.0-1160.el7.x86_64) 7 (Core)' --class centos --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-3.10.0-1160.el7.x86_64-advanced-9cfac9d8-a7d6-46e5-86ff-5843cc384ae6' {
    
        load_video
        set gfxpayload=keep
        insmod gzio
        insmod part_gpt
        insmod xfs
        set root='hd0,gpt2'
        if [ x$feature_platform_search_hint = xy ]; then
          search --no-floppy --fs-uuid --set=root --hint-bios=hd0,gpt2 --hint-efi=hd0,gpt2 --hint-baremetal=ahci0,gpt2 --hint='hd0,gpt2'  3a6bd73c-8f36-4955-b079-b1e2501efc31
        else
          search --no-floppy --fs-uuid --set=root 3a6bd73c-8f36-4955-b079-b1e2501efc31
        fi
        linux16 /vmlinuz-3.10.0-1160.el7.x86_64 root=/dev/mapper/centos-root ro crashkernel=auto spectre_v2=retpoline rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet LANG=zh_CN.UTF-8
        initrd16 /initramfs-3.10.0-1160.el7.x86_64.img
}
menuentry 'CentOS Linux (0-rescue-f5a85e92d51e4d40975fd956fd775f9c) 7 (Core)' --class centos --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-0-rescue-f5a85e92d51e4d40975fd956fd775f9c-advanced-9cfac9d8-a7d6-46e5-86ff-5843cc384ae6' {
    
        load_video
        insmod gzio
        insmod part_gpt
        insmod xfs
        set root='hd0,gpt2'
        if [ x$feature_platform_search_hint = xy ]; then
          search --no-floppy --fs-uuid --set=root --hint-bios=hd0,gpt2 --hint-efi=hd0,gpt2 --hint-baremetal=ahci0,gpt2 --hint='hd0,gpt2'  3a6bd73c-8f36-4955-b079-b1e2501efc31
        else
          search --no-floppy --fs-uuid --set=root 3a6bd73c-8f36-4955-b079-b1e2501efc31
        fi
        linux16 /vmlinuz-0-rescue-f5a85e92d51e4d40975fd956fd775f9c root=/dev/mapper/centos-root ro crashkernel=auto spectre_v2=retpoline rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet
        initrd16 /initramfs-0-rescue-f5a85e92d51e4d40975fd956fd775f9c.img
}
if [ -f  ${config_directory}/custom.cfg ]; then
  source ${config_directory}/custom.cfg
elif [ -z "${config_directory}" -a -f  $prefix/custom.cfg ]; then
  source $prefix/custom.cfg;
fi

开头的set default="${next_entry}"是设置默认的启动项,set timeout=5是设置在启动项选择页面等待的秒数。

开机时候的启动项对应到grub2.cfg中是menuentry这个配置项,menuentry 'CentOS Linux (3.10.0-1160.el7.x86_64) 7 (Core)' --class centos --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-3.10.0-1160.el7.x86_64-advanced-9cfac9d8-a7d6-46e5-86ff-5843cc384ae6'这一串配置中,包含了启动项的名称和命令行参数。

之后的insmod gzio是加载启动项需要的模块。

set root='hd0,gpt2',这是设置启动项执行时的根目录,启动项中的相关文件路径都是相对于这个根目录而言的,比如set root='hd0,gpt2',根目录就是第一个磁盘的第二个分区(sda2),这个分区是gpt分区。initrd16 /initramfs-3.10.0-1160.el7.x86_64.img,这就说明该启动项所使用的虚拟文件系统的镜像位于sda2分区的/initramfs-3.10.0-1160.el7.x86_64.img,我们知道系统完全启动完后sd2实际是挂载到/boot下的,所以这里实际对应的是/boot/initramfs-3.10.0-1160.el7.x86_64.img这个文件。

linux16 /vmlinuz-3.10.0-1160.el7.x86_64这行配置是Linux内核文件,同样需要结合其后设置的root=xxx来进行定位。

grub2配置文件

虽然grub.cfg是grub2的主配置文件,但是我们最好不要直接修改该文件,因为改文件通常是通过grub2-mkconfig这个工具读取相应的配置文件和目录自动生成,如果我们直接在该文件中修改,可能会再之后通过grub2-mkconfig自动生成的时候被覆盖掉。所以正常的方式是在指定的配置文件和目录中修改或添加自定义配置,然后使用grub2-mkconfig生成grub.cfg

下面我们来看这些配置文件和目录。

/etc/default/grub

/etc/default/grub是grub2的主要环境配置文件:

[root@xyz ~]# cat /etc/default/grub
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="crashkernel=auto spectre_v2=retpoline rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet"
GRUB_DISABLE_RECOVERY="true"

这个配置文件中设定了grub2中主要的一些环境变量:

  • GRUB_TIMEOUT

    这个参数的用途是设置开机后启动项选择页面等待的秒数,如果要倒数30秒,可以GRUB_TIMEOUT=30,如果不想等待,可以GRUB_TIMEOUT=0,如果一定要用户手动选择一个启动项后手动启动,可以GRUB_TIMEOUT=-1

  • GRUB_TIMEOUT_STYLE

    这个参数是设置启动项选择页面是否显示菜单:

    • menu:显示菜单
    • countdown:不显示菜单,但显示倒计时
    • hidden:不显示菜单,也不显示倒计时
  • GRUB_TERMINAL_OUTPUT

    这个参数设置使用哪个终端输出grub2界面,可选的值有console, serial, gfxterm, vga_text等,一般为console

  • GRUB_DEFAULT

    这个参数决定默认的启动项是哪个,可以使用的值有saved, 數字, title 名, ID 名等。

    假设当前的启动项设定是这样的:

    menuentry '1st linux system' --id 1st-linux-system {
           ...}
    menuentry '2nd linux system' --id 2nd-linux-system {
           ...}
    menuentry '3rd win system' --id 3rd-win-system {
           ...}
    

    GRUB_DEFAULT=1表示默认启动项是第二个(数字编号从0开始),即2nd linux system这个启动项。GRUB_DEFAULT=3rd-win-system表示默认的启动项是第三个,即3rd win system,这里使用的是id指定启动项。GRUB_DEFAULT=saved表示通过grub2-set-default命令来设置默认启动项,这个命令的默认值是0。

  • GRUB_CMDLINE_LINUX

    如果你的Linux内核启动时需要附加额外参数,可以通过这个环境变量指定,比如:

    GRUB_CMDLINE_LINUX="..... crashkernel=auto rhgb quiet elevator=deadline"
    

    elevator=deadline的作用是调整Linux的I/O调度算法。

下面实践一下:

[root@xyz ~]# cp /etc/default/grub /etc/default/gurb.bak
[root@xyz ~]# vim /etc/default/grub
[root@xyz ~]# diff /etc/default/grub /etc/default/gurb.bak
1c1
< GRUB_TIMEOUT=30
---
> GRUB_TIMEOUT=5
3,4c3
< GRUB_DEFAULT=0
< GRUB_TIMEOUT_STYLE=menu
---
> GRUB_DEFAULT=saved
7c6
< GRUB_CMDLINE_LINUX="crashkernel=auto spectre_v2=retpoline rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet elevator=deadline"
---
> GRUB_CMDLINE_LINUX="crashkernel=auto spectre_v2=retpoline rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet"

这样就修改好/etc/default/grub了,当然修改后要使用工具重新生成grub.cfg

[root@xyz ~]# grub2-mkconfig -o /boot/grub2/grub.cfg
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-3.10.0-1160.el7.x86_64
Found initrd image: /boot/initramfs-3.10.0-1160.el7.x86_64.img
Found linux image: /boot/vmlinuz-0-rescue-f5a85e92d51e4d40975fd956fd775f9c
Found initrd image: /boot/initramfs-0-rescue-f5a85e92d51e4d40975fd956fd775f9c.img
done
[root@xyz ~]# grep timeout /boot/grub2/grub.cfg
if [ x$feature_timeout_style = xy ] ; then
  set timeout_style=menu
  set timeout=30
# Fallback normal timeout code in case the timeout_style feature is
  set timeout=30
[root@xyz ~]# grep default /boot/grub2/grub.cfg
# from /etc/grub.d and settings from /etc/default/grub
   set default="${next_entry}"
   set default="0"
function savedefault {
    
[root@xyz ~]# grep linux16 /boot/grub2/grub.cfg
        linux16 /vmlinuz-3.10.0-1160.el7.x86_64 root=/dev/mapper/centos-root ro crashkernel=auto spectre_v2=retpoline rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet elevator=deadline
        linux16 /vmlinuz-0-rescue-f5a85e92d51e4d40975fd956fd775f9c root=/dev/mapper/centos-root ro crashkernel=auto spectre_v2=retpoline rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet elevator=deadline

可以看到重新生成grub.cfg后,其中的相关配置都改变了。

/etc/grub.d/*

除了直接在/etc/default/grub环境配置文件中修改和添加环境变量来间接影响grub2的主配置文件以外,还可以在/etc/grub.d目录中直接创建配置脚本。事实上该目录下默认就有一些配置脚本,用于生成对应部分的主配置文件相关信息:

[root@xyz ~]# ll /etc/grub.d
总用量 72
-rwxr-xr-x. 1 root root  8702 729 2020 00_header
-rwxr-xr-x. 1 root root  1043 322 2019 00_tuned
-rwxr-xr-x. 1 root root   232 729 2020 01_users
-rwxr-xr-x. 1 root root 10781 729 2020 10_linux
-rwxr-xr-x. 1 root root 10275 729 2020 20_linux_xen
-rwxr-xr-x. 1 root root  2559 729 2020 20_ppc_terminfo
-rwxr-xr-x. 1 root root 11169 729 2020 30_os-prober
-rwxr-xr-x. 1 root root   214 729 2020 40_custom
-rwxr-xr-x. 1 root root   216 729 2020 41_custom
-rw-r--r--. 1 root root   483 729 2020 README

这里说明几个主要的配置脚本的用途:

  • 00_header

    启动项的相关设置,包括屏幕终端的显示格式、启动项菜单倒数时间、是否隐藏启动项等。

  • 10_linux

    尝试找到/boot下面的Linux内核文件和启动内核需要的模块和参数等。

  • 30_os-prober

    扫描所有的磁盘分区,如果分区中有可以作为启动项的grub2中没有的操作系统,将其加入到启动项菜单中。

  • 40_custom

    用户有自己定义的配置脚本,可以添加在这里。

下面来实践一下:

[root@xyz ~]# cd /etc/grub.d
[root@xyz grub.d]# vim 40_custom
[root@xyz grub.d]# cat 40_custom
#!/bin/sh
exec tail -n +3 $0
# This file provides an easy way to add custom menu entries.  Simply type the
# menu entries you want to add after this comment.  Be careful not to change
# the 'exec tail' line above.
menuentry 'My graphical CentOS Linux (3.10.0-1160.el7.x86_64) 7 (Core)' --class centos --class gnu-linux --class gnu --class os --unrestricted --id 'mygraphical' {
    
        load_video
        set gfxpayload=keep
        insmod gzio
        insmod part_gpt
        insmod xfs
        set root='hd0,gpt2'
        if [ x$feature_platform_search_hint = xy ]; then
          search --no-floppy --fs-uuid --set=root --hint-bios=hd0,gpt2 --hint-efi=hd0,gpt2 --hint-baremetal=ahci0,gpt2 --hint='hd0,gpt2'  3a6bd73c-8f36-4955-b079-b1e2501efc31
        else
          search --no-floppy --fs-uuid --set=root 3a6bd73c-8f36-4955-b079-b1e2501efc31
        fi
        linux16 /vmlinuz-3.10.0-1160.el7.x86_64 root=/dev/mapper/centos-root ro crashkernel=auto spectre_v2=retpoline rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet elevator=deadline systemd.unit=graphical.target
        initrd16 /initramfs-3.10.0-1160.el7.x86_64.img
}
[root@xyz grub.d]# grub2-mkconfig -o /boot/grub2/grub.cfg
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-3.10.0-1160.el7.x86_64
Found initrd image: /boot/initramfs-3.10.0-1160.el7.x86_64.img
Found linux image: /boot/vmlinuz-0-rescue-f5a85e92d51e4d40975fd956fd775f9c
Found initrd image: /boot/initramfs-0-rescue-f5a85e92d51e4d40975fd956fd775f9c.img
done

这里添加的内容是拷贝自grub.cfg中的内容并稍加修改所得。

重启以后就能看到这样的启动项:

image-20210908213440591

第三个启动项就是我们通过配置脚本添加的。

移交loader控制权

之前说过,对于安装有多系统的主机,grub2会在用户选择某个系统启动项后移交引导系统启动的职责给对应的loader,这个功能是通过chain loader实现的。

chain loader可以翻译为“引导链”,可以简单看成是“按一定顺序串在一起的启动引导程序”,而grub2可以按照需要将引导系统启动的职责转交给chain loader上的某一个loader。

要在grub2中配置chain loader很简单:

menuentry "Windows" {
    
        insmod chain      # 你得要先載入 chainloader 的模組對吧?
        insmod ntfs       # 建議加入 windows 所在的檔案系統模組較佳!
        set root=(hd0,1)  # 是在哪一個分割槽~最重要的項目!
        chainloader +1    # 請去 boot sector 將 loader 軟體讀出來的意思!
}

假设主机上的磁盘分区情况如下:

[root@study ~]# fdisk -l /dev/vda
   Device Boot      Start         End      Blocks   Id  System
/dev/vda1            2048    10487807     5242880   83  Linux
/dev/vda2   *    10487808   178259967    83886080    7  HPFS/NTFS/exFAT
/dev/vda3       178259968   241174527    31457280   83  Linux

磁盘vda采用MBR分区,且其中第一个分区和第三个分区是Linux系统,第二个分区是Windows系统,如果要手动为Windows分区创建chain loader

[root@study ~]# vim /etc/grub.d/40_custom
menuentry 'Go to Windows 7' --id 'win7' {
    
        insmod chain
        insmod ntfs
        set root=(hd0,msdos2)
        chainloader +1
}
menuentry 'Go to MBR' --id 'mbr' {
    
        insmod chain
        set root=(hd0)
        chainloader +1
}

[root@study ~]# grub2-mkconfig -o /boot/grub2/grub.cfg

以上示例摘抄自《鸟哥的Linux私房菜》

这里Go to MBR这个chain loader的用途是返回grub2的启动项选择界面,所以root=(hd0),没有指定具体分区。

打包 initramfs

在前面已经说明过虚拟文件系统initramfs的作用了,同样说过initramfs在磁盘上是以一个打包文件(镜像文件)的方式保存的,在使用的时候会自动解包到内存中。

一般情况下initramfs文件是随Linux发行版附送的,无需我们手动创建,但如果需要手动创建,需要使用相应的工具进行打包,CentOS 7 中使用的是dracut

[icexmoon@xyz ~]$ cd /tmp
[icexmoon@xyz tmp]$ dracut -v initramfs_test.img $(uname -r)
Executing: /usr/bin/dracut -v initramfs_test.img 3.10.0-1160.el7.x86_64
/usr/bin/dracut: line 1174: /etc/crypttab: Permission denied
dracut module 'busybox' will not be installed, because command 'busybox' could not be found!
dracut module 'dmsquash-live-ntfs' will not be installed, because command 'ntfs-3g' could not be found!
dracut module 'cifs' will not be installed, because command 'mount.cifs' could not be found!
*** Including module: bash ***
*** Including module: nss-softokn ***
...省略

需要注意的是不同的Linux发行版使用的initramfs文件格式可能是不同的,可能并不能通用,所以需要按照相应发行版的打包方式去打包(即可能需要使用不同的工具)。

只要指定生成的文件名和内核版本,就可以创建initramfs文件,很容易。

除了以默认的方式生成initramfs文件,还可以通过其他参数人为给initramfs文件中添加额外的驱动或者文件系统支持:

[icexmoon@xyz tmp]$ sudo dracut -v --add-drivers "e1000e" --filesystems 'ext4 nfs' initramfs_test3.img $(uname -r)
Executing: /sbin/dracut -v --add-drivers e1000e --filesystems "ext4 nfs" initramfs_test3.img 3.10.0-1160.el7.x86_64
...省略
*** Including module: bash ***
*** Including module: nss-softokn ***
*** Including module: i18n ***
*** Including module: network ***
*** Including module: ifcfg ***
*** Including module: drm ***
*** Including module: plymouth ***
*** Including module: dm ***
...省略

测试与安装grub2

如果当前Linux主机使用的引导程序不是grub2,需要安装grub2的话:

[root@xyz ~]# grub2-install /dev/sda
Installing for i386-pc platform.
Installation finished. No error reported.

这样做仅会在/dev/sda磁盘的MBR上安装grub2程序本体,并不会安装相应的配置文件。

如果要在分区的启动扇区(boot sector)中写入grub2,可能会稍微麻烦一些:

[root@xyz ~]# df -T | grep xfs
/dev/mapper/centos-root xfs      10475520 7864560 2610960   76% /
/dev/sda4               xfs       1038336   32992 1005344    4% /srv/myproject
/dev/sda2               xfs       1038336  185940  852396   18% /boot
/dev/mapper/centos-home xfs       5232640 1191744 4040896   23% /home
[root@xyz ~]# grub2-install /dev/sda4
Installing for i386-pc platform.
grub2-install:错误: hd0 中似乎包含一个不为 DOS 引导保留空间的 xfs 文件系统。在此处安装 GRUB 可能导致 grub-setup 覆盖重要数据从而损
坏文件系统(--skip-fs-probe 参数可以禁用这个检查,使用该选项风险自负).
[root@xyz ~]# grub2-install --skip-fs-probe /dev/sda4
Installing for i386-pc platform.
grub2-install: warning: File system `xfs' doesn't support embedding.
grub2-install: warning: 无法嵌入。在此次安装中 GRUB 只能通过使用块列表安装。但是块列表是不可信赖的,不推荐使用。.
grub2-install:错误: will not proceed with blocklists.
[root@xyz ~]# grub2-install --force --recheck --skip-fs-probe /dev/sda4
Installing for i386-pc platform.
grub2-install: warning: File system `xfs' doesn't support embedding.
grub2-install: warning: 无法嵌入。在此次安装中 GRUB 只能通过使用块列表安装。但是块列表是不可信赖的,不推荐使用。.
Installation finished. No error reported.

写入会被阻止,除非加上强制写入的相关参数,比如--force --recheck --skip-fs-probe才会写入。

grub2有一个小细节,如果一个磁盘上有多个分区装有Linux系统,且都是使用的grub2引导,那么MBR上的grub2引导程序会读取哪个分区上的配置文件?答案是最后一个安装的Linux系统。因为Linux安装的时候会覆盖MBR中的grub2程序,自然被覆盖的grub2会读取最后一个安装的Linux所在的分区的配置文件。如果你要改变这一点,当然也无需重新安装Linux,只要启动你希望读取配置文件的所在分区的Linux,然后使用grub2-install重新覆盖磁盘MBR中的引导程序即可。

修改启动项

之前我们说过grub2的一个优点是可以在系统启动的时候编辑启动项:

image-20210909113958878

在启动菜单的任何一个启动项上按下e可以进入启动项的编辑模式:

image-20210909114017870

这个界面输出的内容分为两部分,上边是启动项的实际内容,下边是界面的操作说明。

在编辑完启动项的内容后,如果要取消编辑并返回启动项菜单界面,可以按Ctrl+cESC,如果是要按编辑后的内容启动,可以按Ctrl+x

下面实际测试一下,假如我们要使用默认的启动项进入救援模式(rescue.target):

image-20210909114703340

找到代表内核命令行参数的linux16这一行,在结尾添加上systemd.unit=rescue.target这个参数。

然后按下Ctrl+x执行这个启动项:

image-20210909114914610

可以看到的确进入到了救援模式,要求输入root密码。

不要奇怪,即使是救援模式,CentOS 7也要求输入root密码进行验证。

启动项及终端显示

grub2的配置环境变量中,有一些是和启动项以及终端的显示相关的:

[root@xyz ~]# cp /etc/default/grub /etc/default/grub2.bak
[root@xyz ~]# vim /etc/default/grub
[root@xyz ~]# diff /etc/default/grub /etc/default/grub2.bak
9,11d8
< GRUB_TERMINAL=gfxterm       # 設定主要的終端機顯示為圖形界面!
< GRUB_GFXMODE=1024x768x24    # 圖形界面的 X, Y, 彩度資料
< GRUB_GFXPAYLOAD_LINUX=keep  # 保留圖形界面,不要使用 text 喔!
[root@xyz ~]# grub2-mkconfig -o /boot/grub2/grub.cfg
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-3.10.0-1160.el7.x86_64
Found initrd image: /boot/initramfs-3.10.0-1160.el7.x86_64.img
Found linux image: /boot/vmlinuz-0-rescue-f5a85e92d51e4d40975fd956fd775f9c
Found initrd image: /boot/initramfs-0-rescue-f5a85e92d51e4d40975fd956fd775f9c.img
done

重启后就能看到如下画面:

image-20210909115902679

通过这种方式,启动项菜单就不是只能显示文字了,还可以加载图形,比如企业图标之类的。

为启动项添加密码

有时候我们可能会出于某种安全方面的考虑,希望给启动项添加上密码,需要输入相应的密码后才能编辑或者执行某个启动项。

个人觉得这个功能可能没有太多用处,毕竟Linux作为服务器使用几乎不需要关机和重启,设置密码更就没有什么必要了,反而可能会给自己带来不必要的麻烦。

如果你的确需要这么做,可以直接阅读為個別選單加上密碼

启动过程的问题解决

忘记root密码

不用搜索引擎检索也知道,忘记root密码会是一个Linux用户很常见的问题,毕竟人类这种造物最大的缺陷之一就是记忆容易丢失。

虽然root密码很重要,但也并非是不能进行重置,毕竟如果忘记密码后只能重装系统,那也太让人抓狂了。

在某些版本的Linux发行版中,可能是可以直接进入救援模式(rescue.target)来修改root密码的,但CentOS 7不行,即使是救援模式,也必须要输入root密码才能进入。所以对于CentOS 7,我们需要使用内核参数rd.break进入一个特殊的Linux环境:

image-20210909121318633

和之前进入救援模式类似,在启动项编辑模式中,找到linux16,添加上rd.break参数即可,然后按Ctrl+x启动。

按这种方法启动后就会直接获取到一个有root权限的命令行环境:

image-20210909121521756

但需要注意的是虽然不用输入密码就获取到了root权限,但当前操作系统只是一个称作RAM disk的特殊操作系统,并非我们平时正常从磁盘加载启动的操作系统,可以将当前操作系统看做是Windows装机常会用到的某种“PE”系统。所以在这个环境中直接使用passwd命令修改root密码是没有任何用处的。

需要这样做:

image-20210909122055878

使用mount命令查看挂载点,这里/dev/mapper/centos-root on /sysroot就是我们正常情况下启动系统后挂载的根目录所在分区了,在这个特殊系统中是被挂载到/sysroot下的。

现在我们只要讲当前系统的根目录切换到这个分区,然后使用passwd修改root密码即可,但需要注意的是当前此分区的挂载模式是ro,即只读,因为我们要修改密码,所以自然要先改为rw(读写):

image-20210909122744789

这样就修改好了(出现乱码是因为编码的问题,无视之)。

但现在还不能忙着重启,因为我们之前说过的SELinux的问题,在当前环境下SELInux是关闭的,如果直接重启,因为我们修改了密码相关的文件,就会导致相应文件的SELinux安全上下文出现问题,进而导致无法读取密码文件而无法开机,所以需要让系统在重启后重新创建SELinux的安全上下文:

image-20210909123119878

通过这种方式重启后,需要花很长一段时间来重建SELinux的安全上下文,如果你不想这么做(毕竟我们的修改只牵扯密码相关文件),可以在修改密码后,将/etc/selinux/config中的SELinux的运行模式修改为permissive,让SELinux不强制检查,然后重启。在系统正常启动后,使用root权限执行restorecon -Rv /etc,将/etc下的安全上下文重建即可。再修改/etc/selinux/config中的运行模式为enforcing,然后setenforce 1启用强制检查即可。

当然这种做法会麻烦一些,如果你想省事,就用上边的做法。

除了上面的方法之外,还可以通过直接通过bash来修改root密码,想了解可以阅读直接開機就以 root 執行 bash 的方法

因文件系统错误而无法开机

有时候会因为/etc/fstab设置错误或者因为非正常关机造成的数据不同步,可能会导致无法正常开机。

对于第一种错误,可以通过救援模式或其它方式进入系统后直接修改/etc/fstab文件。而第二种错误,则可以使用xfs_repairfsck.ext3修复文件系统。

以上就是Linux系统启动相关的内容,谢谢阅读。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/hy6533/article/details/120199205

智能推荐

分布式光纤传感器的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告_预计2026年中国分布式传感器市场规模有多大-程序员宅基地

文章浏览阅读3.2k次。本文研究全球与中国市场分布式光纤传感器的发展现状及未来发展趋势,分别从生产和消费的角度分析分布式光纤传感器的主要生产地区、主要消费地区以及主要的生产商。重点分析全球与中国市场的主要厂商产品特点、产品规格、不同规格产品的价格、产量、产值及全球和中国市场主要生产商的市场份额。主要生产商包括:FISO TechnologiesBrugg KabelSensor HighwayOmnisensAFL GlobalQinetiQ GroupLockheed MartinOSENSA Innovati_预计2026年中国分布式传感器市场规模有多大

07_08 常用组合逻辑电路结构——为IC设计的延时估计铺垫_基4布斯算法代码-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏12次。常用组合逻辑电路结构——为IC设计的延时估计铺垫学习目的:估计模块间的delay,确保写的代码的timing 综合能给到多少HZ,以满足需求!_基4布斯算法代码

OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版-程序员宅基地

文章浏览阅读3.3k次,点赞3次,收藏5次。OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版

关于美国计算机奥赛USACO,你想知道的都在这_usaco可以多次提交吗-程序员宅基地

文章浏览阅读2.2k次。USACO自1992年举办,到目前为止已经举办了27届,目的是为了帮助美国信息学国家队选拔IOI的队员,目前逐渐发展为全球热门的线上赛事,成为美国大学申请条件下,含金量相当高的官方竞赛。USACO的比赛成绩可以助力计算机专业留学,越来越多的学生进入了康奈尔,麻省理工,普林斯顿,哈佛和耶鲁等大学,这些同学的共同点是他们都参加了美国计算机科学竞赛(USACO),并且取得过非常好的成绩。适合参赛人群USACO适合国内在读学生有意向申请美国大学的或者想锻炼自己编程能力的同学,高三学生也可以参加12月的第_usaco可以多次提交吗

MySQL存储过程和自定义函数_mysql自定义函数和存储过程-程序员宅基地

文章浏览阅读394次。1.1 存储程序1.2 创建存储过程1.3 创建自定义函数1.3.1 示例1.4 自定义函数和存储过程的区别1.5 变量的使用1.6 定义条件和处理程序1.6.1 定义条件1.6.1.1 示例1.6.2 定义处理程序1.6.2.1 示例1.7 光标的使用1.7.1 声明光标1.7.2 打开光标1.7.3 使用光标1.7.4 关闭光标1.8 流程控制的使用1.8.1 IF语句1.8.2 CASE语句1.8.3 LOOP语句1.8.4 LEAVE语句1.8.5 ITERATE语句1.8.6 REPEAT语句。_mysql自定义函数和存储过程

半导体基础知识与PN结_本征半导体电流为0-程序员宅基地

文章浏览阅读188次。半导体二极管——集成电路最小组成单元。_本征半导体电流为0

随便推点

【Unity3d Shader】水面和岩浆效果_unity 岩浆shader-程序员宅基地

文章浏览阅读2.8k次,点赞3次,收藏18次。游戏水面特效实现方式太多。咱们这边介绍的是一最简单的UV动画(无顶点位移),整个mesh由4个顶点构成。实现了水面效果(左图),不动代码稍微修改下参数和贴图可以实现岩浆效果(右图)。有要思路是1,uv按时间去做正弦波移动2,在1的基础上加个凹凸图混合uv3,在1、2的基础上加个水流方向4,加上对雾效的支持,如没必要请自行删除雾效代码(把包含fog的几行代码删除)S..._unity 岩浆shader

广义线性模型——Logistic回归模型(1)_广义线性回归模型-程序员宅基地

文章浏览阅读5k次。广义线性模型是线性模型的扩展,它通过连接函数建立响应变量的数学期望值与线性组合的预测变量之间的关系。广义线性模型拟合的形式为:其中g(μY)是条件均值的函数(称为连接函数)。另外,你可放松Y为正态分布的假设,改为Y 服从指数分布族中的一种分布即可。设定好连接函数和概率分布后,便可以通过最大似然估计的多次迭代推导出各参数值。在大部分情况下,线性模型就可以通过一系列连续型或类别型预测变量来预测正态分布的响应变量的工作。但是,有时候我们要进行非正态因变量的分析,例如:(1)类别型.._广义线性回归模型

HTML+CSS大作业 环境网页设计与实现(垃圾分类) web前端开发技术 web课程设计 网页规划与设计_垃圾分类网页设计目标怎么写-程序员宅基地

文章浏览阅读69次。环境保护、 保护地球、 校园环保、垃圾分类、绿色家园、等网站的设计与制作。 总结了一些学生网页制作的经验:一般的网页需要融入以下知识点:div+css布局、浮动、定位、高级css、表格、表单及验证、js轮播图、音频 视频 Flash的应用、ul li、下拉导航栏、鼠标划过效果等知识点,网页的风格主题也很全面:如爱好、风景、校园、美食、动漫、游戏、咖啡、音乐、家乡、电影、名人、商城以及个人主页等主题,学生、新手可参考下方页面的布局和设计和HTML源码(有用点赞△) 一套A+的网_垃圾分类网页设计目标怎么写

C# .Net 发布后,把dll全部放在一个文件夹中,让软件目录更整洁_.net dll 全局目录-程序员宅基地

文章浏览阅读614次,点赞7次,收藏11次。之前找到一个修改 exe 中 DLL地址 的方法, 不太好使,虽然能正确启动, 但无法改变 exe 的工作目录,这就影响了.Net 中很多获取 exe 执行目录来拼接的地址 ( 相对路径 ),比如 wwwroot 和 代码中相对目录还有一些复制到目录的普通文件 等等,它们的地址都会指向原来 exe 的目录, 而不是自定义的 “lib” 目录,根本原因就是没有修改 exe 的工作目录这次来搞一个启动程序,把 .net 的所有东西都放在一个文件夹,在文件夹同级的目录制作一个 exe._.net dll 全局目录

BRIEF特征点描述算法_breif description calculation 特征点-程序员宅基地

文章浏览阅读1.5k次。本文为转载,原博客地址:http://blog.csdn.net/hujingshuang/article/details/46910259简介 BRIEF是2010年的一篇名为《BRIEF:Binary Robust Independent Elementary Features》的文章中提出,BRIEF是对已检测到的特征点进行描述,它是一种二进制编码的描述子,摈弃了利用区域灰度..._breif description calculation 特征点

房屋租赁管理系统的设计和实现,SpringBoot计算机毕业设计论文_基于spring boot的房屋租赁系统论文-程序员宅基地

文章浏览阅读4.1k次,点赞21次,收藏79次。本文是《基于SpringBoot的房屋租赁管理系统》的配套原创说明文档,可以给应届毕业生提供格式撰写参考,也可以给开发类似系统的朋友们提供功能业务设计思路。_基于spring boot的房屋租赁系统论文