图源:
Linux启动流程分析
启动流程一览
通常操作系统的启动过程可以分为以下几个步骤:
-
加载BIOS以获取硬件信息并进行自检,这样就可以获取到第一个可启动设备。
-
读取并执行第一个可启动设备的MBR内的启动引导程序(
grub2
、spfdisk
等)。 -
根据启动引导程序的设置加载Kernel,Kernel进行硬件检测并加载需要的驱动和模块。
-
Kernel启动Systemd进程,并且用
default.target
模式准备操作系统环境:-
systemd
执行sysinit.target
初始化系统 -
systemd
执行basic.target
准备基础的操作系统环境 -
systemd
启动multi-user.target
下的本机与服务器服务 -
systemd
执行multi-user.target
下的/etc/rc.d/rc.local
文件 -
systemd
执行multi-user.target
下的getty.target
及登录服务 -
systemd
执行graphical
所需的服务
-
BIOS、boot loader与Kernel加载
BIOS、启动自我检测与MBR/GPT
操作系统启动后第一件事情就是加载BIOS(Basic Input Output System),并通过BIOS加载CMOS信息,由CMOS内设置的信息读取主机的各种硬件信息。
关于BIOS和CMOS的详细信息可以阅读。
从CMOS中可以获取到第一个可用的启动设备(就是我们出重装系统的时候会在BIOS中修改的那个启动顺序列表),然后会从第一个启动设备的MBR分区中获取 boot loader(启动引导程序),如果是多系统,还可能由MBR中的boot loader加载其它分区中的boot loader,再启动具体的操作系统。
boot loader
图源:
所以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的虚拟文件系统压缩包格式比较特殊,所以这里参考编写了一个解包的脚本:
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 9月 7 22:00 bin -> usr/bin
drwxr-xr-x. 2 root root 45 9月 7 22:00 dev
drwxr-xr-x. 12 root root 4096 9月 7 22:00 etc
lrwxrwxrwx. 1 root root 23 9月 7 22:00 init -> usr/lib/systemd/systemd
-rw-------. 1 root root 32089078 9月 7 22:00 initramfs_file
lrwxrwxrwx. 1 root root 7 9月 7 22:00 lib -> usr/lib
lrwxrwxrwx. 1 root root 9 9月 7 22:00 lib64 -> usr/lib64
drwxr-xr-x. 2 root root 6 9月 7 22:00 proc
drwxr-xr-x. 2 root root 6 9月 7 22:00 root
drwxr-xr-x. 2 root root 6 9月 7 22:00 run
lrwxrwxrwx. 1 root root 8 9月 7 22:00 sbin -> usr/sbin
-rwxr-xr-x. 1 root root 3117 9月 7 22:00 shutdown
drwxr-xr-x. 2 root root 6 9月 7 22:00 sys
drwxr-xr-x. 2 root root 6 9月 7 22:00 sysroot
drwxr-xr-x. 2 root root 6 9月 7 22:00 tmp
-rw-r--r--. 1 root root 302 9月 7 21:59 unzip_initramfs.sh
drwxr-xr-x. 7 root root 66 9月 7 22:00 usr
drwxr-xr-x. 2 root root 29 9月 7 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
的方式准备一个基本的操作系统环境,从中我们已经知道,default.target
可以有两个选项:graphical.target
和multi-user.target
,实际上default.target
就是一个到这两者之一的一个alias
。
比较常用的是graphical.target
,并且其本身包含multi-user.target
,所以这里假设当前Linux主机使用的default.target
是graphical.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
比较重要的设置是Requires
和Wants
,前者是说启动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
的启动流程是这样的:
-
启动
local-fs.target
和swap.targete
:这两个target
主要用于挂载本机/etc/fstab
中的文件系统和交换分区。 -
启动
sysinit.target
:这个target
用于检测硬件,并加载所需要的内核模块等。 -
启动
basic.target
:这个target
用于设置基础的操作系统环境,包括加载外围的硬件驱动和防火墙等。 -
启动
multi-user.target
:这个target
用于其他一般系统或网络服务的加载。 -
启动图形界面相关的服务:如
gdm.service
等。
sysinit.target
sysinit.target
的主要工作包含以下部分:
-
特殊文件系统设备的挂载:包括
dev-hugepages.mount
、dev-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 10月 2 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 <maxk@qualcomm.com>
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 7月 24 14:45 device.map
drwxr-xr-x. 2 root root 25 7月 24 14:45 fonts # 字体文件
-rw-r--r--. 1 root root 4309 7月 24 14:47 grub.cfg # grub2的主配置文件
-rw-r--r--. 1 root root 1024 7月 24 14:47 grubenv
drwxr-xr-x. 2 root root 8192 7月 24 14:45 i386-pc # x86计算机所需的grub2模块
drwxr-xr-x. 2 root root 4096 7月 24 14:45 locale # 语言和编码
[root@xyz ~]# ls -l /boot/grub2/i386-pc/
总用量 2388
-rw-r--r--. 1 root root 9936 7月 24 14:45 acpi.mod
-rw-r--r--. 1 root root 1316 7月 24 14:45 adler32.mod
-rw-r--r--. 1 root root 5696 7月 24 14:45 affs.mod
-rw-r--r--. 1 root root 6632 7月 24 14:45 afs.mod
-rw-r--r--. 1 root root 15764 7月 24 14:45 ahci.mod
-rw-r--r--. 1 root root 492 7月 24 14:45 all_video.mod
-rw-r--r--. 1 root root 1028 7月 24 14:45 aout.mod
-rw-r--r--. 1 root root 2932 7月 24 14:45 archelp.mod
-rw-r--r--. 1 root root 5588 7月 24 14:45 ata.mod
-rw-r--r--. 1 root root 4212 7月 24 14:45 at_keyboard.mod
-rw-r--r--. 1 root root 1628 7月 24 14:45 backtrace.mod
-rw-r--r--. 1 root root 7212 7月 24 14:45 bfs.mod
-rw-r--r--. 1 root root 4556 7月 24 14:45 biosdisk.mod
-rw-r--r--. 1 root root 2336 7月 24 14:45 bitmap.mod
-rw-r--r--. 1 root root 3636 7月 24 14:45 bitmap_scale.mod
-rw-r--r--. 1 root root 2160 7月 24 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 7月 29 2020 00_header
-rwxr-xr-x. 1 root root 1043 3月 22 2019 00_tuned
-rwxr-xr-x. 1 root root 232 7月 29 2020 01_users
-rwxr-xr-x. 1 root root 10781 7月 29 2020 10_linux
-rwxr-xr-x. 1 root root 10275 7月 29 2020 20_linux_xen
-rwxr-xr-x. 1 root root 2559 7月 29 2020 20_ppc_terminfo
-rwxr-xr-x. 1 root root 11169 7月 29 2020 30_os-prober
-rwxr-xr-x. 1 root root 214 7月 29 2020 40_custom
-rwxr-xr-x. 1 root root 216 7月 29 2020 41_custom
-rw-r--r--. 1 root root 483 7月 29 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
中的内容并稍加修改所得。
重启以后就能看到这样的启动项:
第三个启动项就是我们通过配置脚本添加的。
移交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的一个优点是可以在系统启动的时候编辑启动项:
在启动菜单的任何一个启动项上按下e
可以进入启动项的编辑模式:
这个界面输出的内容分为两部分,上边是启动项的实际内容,下边是界面的操作说明。
在编辑完启动项的内容后,如果要取消编辑并返回启动项菜单界面,可以按Ctrl+c
或ESC
,如果是要按编辑后的内容启动,可以按Ctrl+x
。
下面实际测试一下,假如我们要使用默认的启动项进入救援模式(rescue.target):
找到代表内核命令行参数的linux16
这一行,在结尾添加上systemd.unit=rescue.target
这个参数。
然后按下Ctrl+x
执行这个启动项:
可以看到的确进入到了救援模式,要求输入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
重启后就能看到如下画面:
通过这种方式,启动项菜单就不是只能显示文字了,还可以加载图形,比如企业图标之类的。
为启动项添加密码
有时候我们可能会出于某种安全方面的考虑,希望给启动项添加上密码,需要输入相应的密码后才能编辑或者执行某个启动项。
个人觉得这个功能可能没有太多用处,毕竟Linux作为服务器使用几乎不需要关机和重启,设置密码更就没有什么必要了,反而可能会给自己带来不必要的麻烦。
如果你的确需要这么做,可以直接阅读。
启动过程的问题解决
忘记root密码
不用搜索引擎检索也知道,忘记root
密码会是一个Linux用户很常见的问题,毕竟人类这种造物最大的缺陷之一就是记忆容易丢失。
虽然root
密码很重要,但也并非是不能进行重置,毕竟如果忘记密码后只能重装系统,那也太让人抓狂了。
在某些版本的Linux发行版中,可能是可以直接进入救援模式(rescue.target)来修改root
密码的,但CentOS 7不行,即使是救援模式,也必须要输入root
密码才能进入。所以对于CentOS 7,我们需要使用内核参数rd.break
进入一个特殊的Linux环境:
和之前进入救援模式类似,在启动项编辑模式中,找到linux16
,添加上rd.break
参数即可,然后按Ctrl+x
启动。
按这种方法启动后就会直接获取到一个有root
权限的命令行环境:
但需要注意的是虽然不用输入密码就获取到了root
权限,但当前操作系统只是一个称作RAM disk
的特殊操作系统,并非我们平时正常从磁盘加载启动的操作系统,可以将当前操作系统看做是Windows装机常会用到的某种“PE”系统。所以在这个环境中直接使用passwd
命令修改root
密码是没有任何用处的。
需要这样做:
使用mount
命令查看挂载点,这里/dev/mapper/centos-root on /sysroot
就是我们正常情况下启动系统后挂载的根目录所在分区了,在这个特殊系统中是被挂载到/sysroot
下的。
现在我们只要讲当前系统的根目录切换到这个分区,然后使用passwd
修改root
密码即可,但需要注意的是当前此分区的挂载模式是ro
,即只读,因为我们要修改密码,所以自然要先改为rw
(读写):
这样就修改好了(出现乱码是因为编码的问题,无视之)。
但现在还不能忙着重启,因为我们之前说过的SELinux的问题,在当前环境下SELInux是关闭的,如果直接重启,因为我们修改了密码相关的文件,就会导致相应文件的SELinux安全上下文出现问题,进而导致无法读取密码文件而无法开机,所以需要让系统在重启后重新创建SELinux的安全上下文:
通过这种方式重启后,需要花很长一段时间来重建SELinux的安全上下文,如果你不想这么做(毕竟我们的修改只牵扯密码相关文件),可以在修改密码后,将/etc/selinux/config
中的SELinux的运行模式修改为permissive,让SELinux不强制检查,然后重启。在系统正常启动后,使用root
权限执行restorecon -Rv /etc
,将/etc
下的安全上下文重建即可。再修改/etc/selinux/config
中的运行模式为enforcing
,然后setenforce 1
启用强制检查即可。
当然这种做法会麻烦一些,如果你想省事,就用上边的做法。
除了上面的方法之外,还可以通过直接通过bash
来修改root
密码,想了解可以阅读。
因文件系统错误而无法开机
有时候会因为/etc/fstab
设置错误或者因为非正常关机造成的数据不同步,可能会导致无法正常开机。
对于第一种错误,可以通过救援模式或其它方式进入系统后直接修改/etc/fstab
文件。而第二种错误,则可以使用xfs_repair
或fsck.ext3
修复文件系统。
以上就是Linux系统启动相关的内容,谢谢阅读。
文章评论