第19章 开机流程、模块管理与Loader
第19章 开机流程、模块管理与Loader
19.1 Linux的开机流程分析
19.1.1 开机流程一览
简单来说,系统开机的经过可以汇整成底下的流程的:
- 加载BIOS的硬件信息与进行自我测试,并依据设定取得第一个可开机的装置
- 读取并执行第一个开机装置内MBR的boot Loader(亦即是grub2,spfdisk等程序)。
- 依据bootloader的设定加载Kernel,Kernel会开始侦测硬件与加载驱动程序。
- 在硬件驱动成功后,Kernel会主动呼叫systemd程序,并以default.target流程开机。
- systemd执行sysinit.target初始化系统及basic.target准备操作系统;
- systemd启动multi-user.target下的本机与服务器服务;
- systemd执行multi-user.target下的/etc/rc.d/rc.local文件;
- systemd执行multi-user.target下的getty.target及登入服务
- systemd执行graphical需要的服务
19.1.2 BIOS,boot loader与kernel载入
这里为了讲解方便,将后续会用到的专有名词先做个综合解释:
-
BIOS: 不论传统BIOS还是UEFI BIOS都会被简称为BIOS
-
MBR: 虽然分区表有传统MBR以及新式GPT,不过GPT也有保留一块兼容MBR的区块,因此,底下的说明在安装boot loader的部分,鸟哥还是简称为MBR喔!总之,MBR就代表该磁盘的最前面可安装boot loader的那个区块就对了!
-
Boot Loader的功能
- 提供选单
- 载入核心文件
- 转交其他loader
-
加载核心侦测硬件与initramfs的功能
虚拟文件系统(Initial RAM Disk或Initial RAM Filesystem)一般使用的档名为/boot/initrd或/boot/initramfs,这个文件的特色是,他也能够透过boot loader来加载到内存中,然后这个文件会被解压缩并且在内存当中仿真成一个根目录,且此仿真在内存当中的文件系统能够提供一支可执行的程序,透过该程序来加载开机过程中最需要的核心模块,通常这些模块就是USB,RAID,LVM,SCSI等文件系统与磁盘接口的驱动程序啦!等载入完成后,会帮忙核心重新呼叫systemd来开始后续的正常开机流程。
19.1.3 第一支程序systemd及使用default.target进入开机程序分析
19.1.7 开机过程会用到的主要配置文件
- 关于模块:/etc/modprobe.d/*.conf及/etc/modules-load.d/*.conf
- /etc/sysconfig/*
- authconfig
- cpupower
- firewalld,iptables-config,ebtables-config
- network-scripts/
19.2 核心与核心模块
核心与核心模块放在哪儿?
- 核心: /boot/vmlinuz或 /boot/vmlinuz-version
- 核心解压缩所需RAM Disk: /boot/initramfs(/boot/initramfs-version)
- 核心模块: /lib/modules/version/kernel或/lib/moudles/$(uname -r)/kernel
- 核心原始码:/usr/src/linux或/usr/src/kernels(要安装才会有,预设不安装)
如果该核心被顺利的加载系统当中了,那么就会有几个信息记录下来:
- 核心版本: /proc/version
- 系统核心功能: /proc/sys/kernel/
问题来啦,如果我有个新的硬件,偏偏我的操作系统不支持,该怎么办?很简单啊!
- 重新编译核心,并加入最新的硬件驱动程序原始码
- 将该硬件的驱动程序编译成为模块,在开机时加载该模块
19.2.1 核心模块与相依性
既然要处理核心模块,自然就得要了解我们核心提供的模块之间的相关性啦!基本上,核心模块的放置处是在/lib/modules/$(uname -r)/kernel当中,里面主要还分成几个目录:
- arch: 与硬件平台有关的项目,例如CPU的等级等等
- crypto: 核心所支持的加密的技术,例如md5或者是des等等。
- drivers: 一些硬件的驱动程序,例如显示适配器、网络卡、PCI相关硬件等等
- fs: 核心所支持的filesystems,例如vfat,reiserfs,nfs等等
- lib: 一些函式库
- net: 与网络有关的各项协议数据,还有防火墙模块(net/ipv4/netfilter/*)等等
- sound: 与音效有关的各项模块
如果要我们一个一个的去检查这些模块的主要信息,然后定义出他们的相依性,我们可能会疯掉吧!所以说,我们的Linux当然会提供一些模块相依性的解决方案啰!对啦!那就是检查/lib/modules/$(uname -r)/modules.dep这个文件啦!他记录了在核心支持的模块的各项相依性。
那么,这个文件如何建立呢?挺简单!利用depmod这个指令就可以达到建立该文件的需求了!
depmod [-Ane]
选项与参数:
-A: 不加任何参数时,depmod会主动的去分析目前核心的模块,并且重新写入/lib/modules/$(uname -r)/modules.dep当中。若加入-A参数时,则depmod会去搜寻比modules.dep内还要新的模块,如果真找到新模块,才会更新。
-n: 不写入modules.dep,而是将结果输出到屏幕上(standard out)
-e: 显示出目前已加载的不可执行的模块名称
19.2.2 核心模块的观察
那你到底晓不晓得目前核心加载了多少的模块呢?粉简单啦!利用lsmod即可!
lsmod
了解模块之间的相依性。
modinfo [-adln][module_name][filename]
选项与参数:
-a: 仅列出作者名称
-d: 仅列出该modules的说明(description)
-l: 仅列出授权(license)
-n: 仅列出该模块的详细路径。
19.2.3 核心模块的加载与移除
好了,如果我想要自行手动加载模块,又该如何是好?有很多方法啦,最简单而且建议的,是使用modprobe这个指令来加载模块,这是因为modprobe会主动的去搜寻modules.dep的内容,先克服了模块的相依性后,才决定需要加载的模块有哪些,很方便。至于insmod则完全由使用者自行加载一个完整文件名的模块,并不会主动的分析模块相依性啊!
insmod [/full/path/module_name][parameters]
insmod立刻就将该模块加载啰~但是insmod后面接的模块必须是完整的“档名”才行!那如何移除这个模块呢?
rmmod [-fw] module_name
选项与参数:
-f: 强制将该模块移除掉,不论是否正被使用
使用insmod与rmmod的问题就是,你必须要自行找到模块的完整文件才行,而且如同上述范例二的结果,万一模块有相依属性的问题时,你将无法直接加载或移除该模块呢!所以近年来我们都建议直接使用modprobe来处理模块加载的问题,这个指令的用法是:
modprobe [-cfr] module_name
选项与参数:
-c: 列出目前系统所有的模块!(更详细的代号对应表)
-f: 强制加载该模块
-r: 类似rmmod,就是移除某个模块啰~
19.3 Boot Loader:Grub2
"boot loader"是载入核心的重要工具,没有boot loader的话,那么kernel根本就没有办法被系统加载的呢!所以,底下我们先谈一谈boot loader的功能,然后再讲一讲现阶段Linux里头最主流的grub2这个boot loader吧!
19.3.1 boot loader的两个stage
我们都知道,MBR是整个硬盘的第一个sector内的一个区块,充其量整个大小也才446bytes而已。即使是GPT也没有很大的扇区来储存loader的数据。我们的loader功能这么强,光是程序代码与设定数据不可能只占这么一点点的容量吧?那如何安装?
为了解决这个问题,所以Linux将boot loader的程序代码执行与设定值加载分成两个阶段(stage)来执行:
- Stage 1: 执行boot loader主程序:
第一阶段为执行boot loader的主程序,这个主程序必须要被安装在开机区,亦即是MBR或者是boot sector。但如前撰述,因为MBR实在太小了,所以,MBR或boot sector通常仅安装boot loader的最小主程序,并没有安装loader的相关配置文件 - stage 2: 主程序加载配置文件
第二阶段为透过boot loader加载所有配置文件与相关的环境参数文件(包括文件系统定义与主要配置文件grub.cfg),一般来说,配置文件都在/boot底下。
那么这些配置文件都放在哪里啊?这些与grub2有关的文件都放置到/boot/grub2中,那我们就来看看有哪些文件吧!
19.3.2 grub2的配置文件 /boot/grub2/grub.cfg 初探
grub2的优点挺多的,包括有:
- 认识与支持较多的文件系统,并且可以使用grub2的主程序直接在文件系统中搜寻核心档名
- 开机的时候,可以“自行编辑与修改开机设定项目”,类似bash的指令模式
- 可以动态搜寻配置文件,而不需要在修改配置文件后重新安装grub2。亦即是我们只要修改完/boot/grub2/grub.cfg里头的设定后,下次开机后就生效了。
- 磁盘与分区槽在grub2中的代号
grub2对硬盘的代号设定与传统的Linux磁盘代号可完全是不同的!grub2对硬盘的识别使用的是如下的代号:
(hd0,1) # 一般的默认语法,由grub2自动判断分区格式
(hd0,msdos1) # 此磁盘的分区为传统的MBR模式
(hd0,gpt1) # 此磁盘的分区为GPT模式
需要注意以下几个东西:
- 硬盘代号以小括号()包起来
- 硬盘以hd表示,后面接一组数字
- 以“搜寻顺序”作为硬盘的编号!(这个重要)
- 第一个搜寻到的硬盘为0号,第二个为1号,以此类推
- 每颗硬盘的第一个partition代号为1,依序类推
- /boot/grub2/grub.cfg配置文件(重点在了解,不要随便改!)
基本上,grub2不希望你自己修改grub.cfg这个配置文件,取而代之的是修改几个特定的配置文件之后,由grub2-mkconfig这个指令来产生新的grub.cfg文件。不过,你还是得要了解一下grub2.cfg的大致内容。
在grub.cfg最开始的部分,其实大多是环境设定与默认值设定等,比较重要的当然是默认由哪个选项开机(set default)以及预设的秒数(set timeout),再来则是每一个选单的设定,就是在"menuentry"这个设定值之后的项目啰!
在menuentry之后会有几个项目的规范,包括"--class,--unrestricted --id"等等的指定项目,之后透过"{}"将这个选单会用到的数据框起来,在选择这个选单之后就会进行括号内的动作的意思。如果真的点选了这个选单,那grub2首先会加载模块,例如上表中的"load_video,insmod gzio,insmod part_gpt,insmod xfs"等等的项目,都是在加载要读取核心文件所需要的磁盘、分区槽、文件系统、解压缩等等的驱动程序。之后就是三个比较重要的项目:- set root='hd0,gpt2'
这个root是指定grub2配置文件所在的那个装置。 - linux16 /vmlinuz-... root=/dev/mapper/centos-root...
这个就是Linux核心文件以及核心执行时所下达的参数。你应该比较奇怪的是,我们的核心文件不是/boot/vmlinuz-xxx吗?怎么这里的设定会是在根目录呢?这个跟上面的root有关啦!大部分的系统大多有/boot这个分区槽,如果/boot没有分区,那会怎么回事呢?我们用底下的迭代来说明一下:- 如果没有/boot分区,仅有/分区:所以档名会这样变化喔:
/boot/vmlinuz-xxx --> (/)/boot/vmlinuz-xxx -->(hd0,msdos1)/boot/vmlinuz-xxx - 如果/boot是独立分区,则档名的变化会是这样:
/boot/vmlinuz-xxx --> (/boot)/vmlinuz-xxx --> (hd0,msods1)/vmlinuz-xxx
因此,这个linux16后面接的档名要跟上面的root搭配在一起,才是完整的绝对路径文件名喔!看懂了吗?至于linux16/vmlinuz-xxx root=/file/name那个root指的是“linux文件系统中,根目录是在哪个装置上”的意思!从本意一开始的开机流程中,我们就知道核心会主动去挂载根目录,并且从根目录中读取配置文件,再进一步开始开机流程。所以,核心文件后面一定要接根目录的装置啊!这样理解吧?我们从/etc/fstab里面也知道根目录的挂载可以是装置文件名、UUID与LABEL名称,因此这个root后面也是可以带入类似root=UUID=1111.2222.33...之类的模式喔!
- 如果没有/boot分区,仅有/分区:所以档名会这样变化喔:
- initrd16/initramfs-3.10...
这个就是initramfs所在的档名,跟linux16那个vmlinuz-xxx相同,这个档名也是需要搭配"set root=xxx"那个项目的装置,才会得到正确的位置喔!
- set root='hd0,gpt2'
19.3.3 grub2配置文件维护/etc/default/grub与/etc/grub.d
前一个小节我们谈到的是grub2的主配置文件grub.cfg约略的内容,但是因为该文件的内容太过复杂,数据量非常庞大,grub2官方说明不建议我们手动修改!而是应该透过/etc/default/grub这个主要环境配置文件与/etc/grub.d目录内的相关配置文件来处理比较妥当!
- /etc/default/grub 主要环境配置文件
我们底下主要谈谈几个重要的设定项目而已。现在我们来说说处理的项目重点吧!- 倒数时间参数:GRUB_TIMEOUT
- 是否隐藏选单项目:GRUB_TIMEOUT_STYLE
这个项目可选择的设定值有menu,countdown,hidden等等。如果没有设定,预设是menu的意思。这个项目主要是在设定要不要显示选单! - 讯息输出的终端机模式:GRUB_TERMINAL_OUTPUT
这个项目是指定输出的画面应该使用哪一个终端机来显示的意思,主要的设定值有"console,serial,gfxterm,vga_text"等等。 - 默认开机选单项目:GRUB_DEFAULT
这个项目在指定要用哪一个选单(menuentry)来作为默认的开机项目的意思。能使用的设定值包括有“saved,数字,title名,ID名等。(如果使用数字,从0开始。 - 核心的外加参数功能:GRUB_COMLINE_LINUX
如果你的核心在启动的时候还需要加入额外的参数,就在这里加入吧!
这个主要环境配置文件编写完毕之后,必须要使用grub2-mkconfig来重建grub.cfg才行喔!因为主配置文件就是grub.cfg而已。我们是通过许多脚本的协力来完成grub.cfg的自动建置。当然啰,额外的自己设定的项目,就是写入/etc/default/grub文件内就是了。
- 选单建置的脚本 /etc/grub.d/*
grub2-mkconfig执行之后,屏幕怎么会主动的去抓到Linux的核心,还能够找到对应核心版本的initramfs呢?怎么这么厉害?其实grub2-mkconfig会去分析/etc/grub.d/里面的文件,然后执行该文件来建置grub.cfg的啦!所以啰,/etc/grub.d/里面的文件就显得很重要了。一般来说,该目录下会有这些文件存在:- 00_header: 主要在建立初始的显示项目,包括需要加载的模块分析、屏幕终端机的格式,倒数秒数、选单是否需要隐藏等等,大部分在/etc/default/grub里面所设定的变量,大概都会在这个脚本当中被利用来重建grub.cfg。
- 10_linux: 根据分析/boot底下的文件,尝试找到正确的linux核心与读取这个核心需要的文件系统模块与参数等等,都在这个脚本动作后找到并设定到grub.cfg当中。
- 30_os-prober:这个脚本默认会到系统上找其他的partition里面可能含有的操作系统,然后将该操作系统做成选单来处理就是了。如果你不想要让其他的操作系统被侦测到并拿来开机,那可以在/etc/default/grub里面加上“GRUB_DISABLE_OS_PROBER=true”取消这个文件的运作。
- 40_custom: 如果你还有其他想要自己手动加上去的选单项目,或者是其他的需求,那么建议在这里补充即可!
所以,一般来说,我们会更动到的就是仅有40_custom这个文件即可。这个文件内容也大多在放置管理员自己想要加进来的选单项目就是了。好了,那问题来了,我们知道menuentry就是一个选单,那后续的项目有哪些东西呢?简单的说,就是这个menuentry有几种常见的设定?亦即是menuentry的功能啦!常见的有这几样:- 直接指定核心开机
- 透过chainloader的方式移交loader控制权
19.3.5 测试与安装grub2
grub2-install [--boot-directory=DIR] INSTALL_DEVICE
选项与参数:
--boot-directory=DIR那个为实际的目录,使用grub2-install预设会将grub2所有的文件都复制到/boot/grub2/*,如果想要复制到其他目录与装置去,就得要用这个参数。
INSTALL_DEVICE安装的装置代号啦!
19.3.6 开机前的额外功能修改
如果你正在进行开机,那么请注意,我们可以在预设选单(鸟哥的范例当中是40秒)按下任意键,还可以进行grub2的“在线编修”功能喔!
因为CentOS 7预设没有提供美美的底图给我们使用,因此这里会看到无法分辨的两个区块!事实上这真的是两个区块,上方是实际你可以编辑的内容区段,仔细看,这不就是我们在grub.cfg里面设定的东西吗?没错!此时你还可以继续进一步修改喔!用上/下/左/右按键到你想要编辑的地方,直接删除、新增即可!
至于下方画面则仅是一些编辑说明,重点在告诉你,编辑完毕之后,若想要取消而回到前一个画面,请使用[ctrl]+c或者是[esc]回去,若是修改完毕,想要直接开机时,请使用[ctrl]+x来开机啰!
问:
现在我想要让系统开机的过程中,让这个系统进入救援模式(rescue),而不想要进入系统后使用systemctl rescue时,该如何处理?
答:
仔细看到图19.3.2的画面,按下‘向下’的箭头键,直到出现linux16那一行,然后在那一行的最后加上systemd.unit=rescue.target,画面有点像这样:
然后再按下[ctrl]+x来进入系统,就能够取得rescue的环境了!登入后有点像这样:
你可能会觉得很讶异!早期SystemV的系统中,进入runlevel 1的状态是不需要输入root密码的,在systemd的年代,哇!竟然需要密码才能够进入救援模式耶!而且是强制要有root密码耶!如果你是root密码忘记要救援,救个鬼啊~还是需要root密码啊!那怎办?没关系~本章稍后会告诉你应该要如何处理的啦!
19.3.7 关于开机画面与终端机画面的图形显示方式
如果你想要让你的开机画面使用图形显示方式,例如使用中文来显示你的画面啊!因为我们预设的locale语系就是zh_TW.utf8嘛!所以理论上grub2会显示中文出来才对啊!有没有办法达成呢?是有的~透过图形显示的方法即可!不过,我们得要重新修改grub.cfg才行喔!依据底下的方式来处理:
# 先修改重要的配置文件
vim /etc/default/grub
......(前面省略).....
GRUB_TERMINAL=gfxterm # 设定主要的终端机显示为图形界面
GRUB_GFXMODE=1024*768*24 #图形界面的X,Y,彩度资料
GRUB_GFXPAYLOAD_LINUX=keep #保留图形界面,不要使用text喔!
# 重新建立配置文件
grub2-mkconfig -o /boot/grub2/grub.cfg
19.4 开机过程的问题解决
19.4.1 忘记root密码的解决之道
其实在Linux环境中root密码忘记时还是可以救回来的!只要能够进入并且挂载 / ,然后重新设定一下root的密码,就救回来啦!
只是新版的systemd的管理机制中,默认的rescue模式是无法直接取得root权限的喔!还是得要使用root的密码才能够登入rescue环境耶!天哪!那怎办?没关系,还是有办法滴~透过一个名为‘rd.break’的核心参数来处理即可喔!只是需要注意的是,rd.break是在Ram Disk里面的操作系统状态,因此你不能直接取得原本的linux系统操作环境。所以,还需要chroot的支持!更由于SELinux的问题,你可能还得要加上某些特殊的流程才能顺利的搞定root密码的救援喔!
现在就让我们来实作一下吧!(1)按下systemctl reboot来重新启动,(2)进入到开机画面,在可以开机的选单上按下 e 来进入编辑模式,然后就在linux16的那个核心项目上的最后加上' rd.break'这个参数
改完之后按下[ctrl]+x开始开机,开机完成后屏幕会出现如下的类似画面,此时请注意,你应该是在RAM Disk的环境,并不是原本的环境,因此根据目录底下的东西跟你原本的系统无关喔!而且,你的系统应该会被挂载到/sysroot目录下,因此,你得要这样作:
mount # 检查一下挂载点!一定会发现/sysroot才是对的!
mount -o remount,rw /sysroot # 要先让它挂载成可擦写!
chroot /sysroot #实际切换了根目录的所在!取回你的环境了!
echo "your_root_new_pw" | passwd --stdin root
touch /.autorelabel # 很重要!变回SELinux的安全文本~
exit
reboot
上述的流程你应该没啥大问题才对~比较不懂的,应该是(1)chroot是啥?(2)为何需要/.autorelabel这个文件?
- chroot目录:代表你的根目录“暂时”切换到chroot之后所接的目录。因此,以上表为例,那个/sysroot将会被暂时作为根目录,而我们知道那个目录其实就是最原先的系统根目录,所以你当然就能够用来处理你的文件系统与相关的帐号管理啰!
- 为何需要/.autorelabel:在rd.break的RAM Disk环境下,系统是没有SELinux的,而你刚刚更改了/etc/shadow(因为改密码啊!),所以“这个文件的SELinux安全文本的特性将会被取消”喔!如果你没有让系统于开机时自动的恢复SELinux的安全文本,你的系统将产生“无法登入”的问题。加上 /.autorelabel 就是要让系统在开机的时候使用预设的SELinux type重新写入SELinux安全文本到每个文件去!。
不过,加上 /.autorelabel 之后,系统在开机就会重新写入SELinux的type到每个文件,因此会花不少的时间喔!如果你不想要花太多时间,还有个方法可以处理:
- 在 rd.break 模式下,修改完root密码后,将/etc/selinux/config内的SELinux类型改为permissive
- 重新启动后,使用root的身份下达“restore -Rv /etc”仅修改/etc底下的文件
- 重新修改/etc/selinux/config改回enforcing,然后“setenforce”即可!
19.4.2 直接开机就以root执行bash的方法
除了上述的rd.break之外,我们还可以直接开机取得系统根目录后,让系统直接丢一个bash给我们使用喔!使用的方法很简单,就同样在开机的过程中,同在linux16的那一行,最后面不要使用【rd.break】而是使用【init=/bin/bash】即可!最后开机完成后就会丢一个bash给我们,同样不需要root密码而有root权限!
但是要完整的操作该系统是不可能的,因为我们将PID一号更改为bash啦!所以,最多还是用在救援方面就是了!而且,同样的,要操作该系统你还是得要remount根目录才行喔!否则无法更改文件系统啦!基本上,这个系统的处理方法是要这样作的:
mount -o remount,rw /
echo "your_root_pw" | passwd --stdin root
reboot
/sbin/reboot
pstree -p
如上面的操作,你会发现由于是最预设的bash环境,所以连PATH都仅有/bin而已~所以你不能下达reboot!同时,由于没有systemd或者是init的存在,所以真的使用绝对路径来下达reboot时,系统也是无法协助你重新启动啦!此时你只能按下reset或者是强制关机后,才能再次开机!所以...感觉上还是rd.break比较保险...
同时请注意,鸟哥上面刻意忘记处理/.autorelabel的文件建置~你如果按照鸟哥上述的方法实作的话,嘿嘿,此时应该是无法登入的哦!请重新启动进入rd.break模式,然后使用SELinux改为permissive的方法来实验看看。等到可以顺利以root登入系统后,使用restorecon -Rv /etc来瞧一瞧,应该会像底下这样:
# getenforce
# restorecon -Rv /etc/
# vim /etc/selinux/config
SELinux=enforcing
# setenforce 1
19.4.3 因文件系统错误而无法开机
如果因为设定错误导致无法开机时要怎么办啊?这就更简单了!最容易出错的设定而导致无法顺利开机的步骤,通常就是/etc/fstab这个文件了,尤其就是/etc/fstab这个文件了。这种情况的问题大多画面如下面的画面所示:
Checking filesystems
fsck.ext3 Invalid argument while trying to open /dev/md0
...
...
Give root password for maintenance(or type Control-D to continue):
看到最后两行,他说可以输入root的密码继续加以救援哦!那请输入root的密码来取得bash并以 mount -o remount,rw /
将目录挂载成可擦写后,继续处理吧!其实会造成上述画面可能的原因除了/etc/fstab编辑错误之外,如果你曾经不正常关机后,也可能导致文件系统不一致(Inconsistent)的情况,如果是扇区错乱的话,请看到上图中的第二行处,fsck告知其实是/dev/md0出错,此时你就应该要利用fsck.ext3去检测/dev/md0才是!等到系统发现错误,并且出现【clear [Y/N]】时,输入【y】吧!
当然啦!如果是XFS文件系统的话,可能就得要使用【xfs_repair】这个指令来处理。