1. Linux开机流程
简单来说,系统开机的过程如下:
- 加载BIOS 的硬件信息与进行自我测试,并依据设定取得第一个可开机的装置;
- 读取并执行第一个开机装置内MBR 的boot Loader (亦即是grub2, spfdisk 等程序);
- 依据boot loader 的设定加载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 需要的服务
1.1 boot loader功能
- 提供选单:用户可以选择不同的开机项目,这也是多重引导的重要功能;
- ★载入核心文件:直接指向可开机的程序区段来开始操作系统;
- 转交其他loader:将开机管理功能转交给其他loader 负责。
Loader 的最主要功能是要认识操作系统的文件格式并据以加载核心到主存储器中去执行。由于不同操作系统的文件格式不一致,因此每种操作系统都有自己的boot loader。
其实每个文件系统 (filesystem,或者是partition) 都会保留一块启动扇区(boot sector) 提供操作系统安装 boot loader , 而通常操作系统默认都会安装一份 loader 到他根目录所在的文件系统的 boot sector 上。每个操作系统默认是会安装一套 boot loader 到他自己的文件系统中(就是每个filesystem 左下角的方框),而在Linux 系统安装时,可选择将boot loader 安装到MBR 去,也可以选择不安装。而Windows会在MBR和boot sector都安装上。
由于具有选单功能,所以可以选择不同的内核进行开机;由于具有控制权转交功能,所以可以加载不同boot sector内的loader。但Windows的loader预设不具有控制权转交功能,所以不能使用Win的loader来加载Linux的loader,因此多重操作系统安装中强调先装Windows再装Linux。下图是开机管理程序的选单功能与控制权转交功能示意图。
其中有三个选单,MBR中使用Linux的grub2开机管理程序,第一个选单是直接加载Linux内核开机;第二个选单将开机管理程控权交给Windows来管理;第三个选单使用Linux在boot sector内的开机管理程序。具体流程是:
● 选单一:MBR(grub2) --> kernel file --> booting
● 选单二:MBR(grub2) --> boot sector(Windows loader) --> Windows kernel --> booting
● 选单三:MBR(grub2) --> boot sector(grub2) --> kernel file --> booting
1.2 加载核心、侦测硬件与initramfs的功能
@加载核心、侦测硬件
借助boot loader的管理读取内核文件后,Linux就会将内核解压缩到内存当中,利用内核功能,测试与驱动各周边装置。此时,内核开始接管BIOS后的工作,其会以自己的功能重新侦测一次硬件。内核文件一般以/boot/vmlinuz
形式存在。/boot
目录下的内容可以通过ls --format=single-column -F /boot
指令进行查看:
为了硬件开发商和内核功能开发者的便利,Linux内核是可以通过动态加载内核模块的,这些内核模块放置在/lib/modules/
当中(注意/lib
不可以与/
分别放置在不同分区中)。因此在开机的过程中内核必须要挂载根目录,这样才能够读取内核模块提供加载驱动程序的功能。
@模块加载的问题
一般而言,非必要功能可编译为模块的内核功能。因此USB,SATA, SCSI等磁盘驱动程序通常以模块形式存在。但是内核在没有SATA磁盘驱动程序的情况下,无法侦测到磁盘并挂载根目录,而SATA驱动程序在/lib/modules内,无法读取。这个情况导致Linux无法顺利开机。
@模块加载问题解决办法及initramfs功能
为解决这个问题引入了虚拟文件系统(Initial RAM Disk or Initial RAM Filesystem) 一般文件名为/boot/initrd
(initial RAM Disk)或/boot/initramfs
。其能够通过boot loader加载到内存中,并使用gzip
和cpio
解压在内存中仿真成一根目录,其在内存当中的文件系统可提供一个可执行程序,加载开机过程中所须模块,通常为USB, RAID, LVM, SCSI等。等载入完成,协助内核重新调用systemd进行后续的正常启动流程。
对于/boot/initramfs
文件,可用lsinitrd
指令查看其中的文件内容,其中含有两大区块,一个是事先声明的数据,包括kernel/x86/microcode/GenuineIntel.bin等;一个是内核会读取的重要文件,观察发现init程序已被systemd程序所取代。
其实initramfs 就是一个小型的根目录,其通过systemd 来进行管理,同时观察default.target 的链接,发现该小型系统通过initrd.target 来开机,而initrd.target 也是需要读入一堆例如basic.target, sysinit.target 等等的硬件侦测、核心功能启用的流程, 然后开始让系统顺利运作。最终卸除 initramfs 的小型文件系统,实际挂载系统的根目录。
1.3 PID=1的systemd程序及其操作环境default.target
内核加载完毕、进行过硬件侦测与驱动程序加载后,此时主机硬件已经ready。此时内核呼叫第一支程序systemd,所以其pid为1。systemd 最主要的功能就是准备软件执行的环境,包括系统的主机名、网络设定、语系处理、文件系统格式及其他服务的启动等。systemd所有的动作都会通过systemd 的默认启动服务集合/etc/systemd/system/default.target 的规划进行。
另外,systemd 已经舍弃沿用多年的system V 的runlevel 。但是systemd对runlevel概念进行了部分兼容。可使用ll -d /usr/lib/systemd/system/runlevel*.target | cut -c 28-
指令查询两者之间的对应关系:
system V可使用init 3切换到文字界面,init 5转成图形界面。相关的对应关系为:
@相容 system V 的 rc-local.service
对于system V而言,若在系统完成开机后,想让系统额外执行某些程序,可将这些程序或脚本的绝对路径写入/etc/rc.d/rc.local文件中。在新的systemd机制中,建议将脚本配置文件写入/etc/systemd/system目录中。systemd中对于原先机制的兼容是rc-local.service服务,这个服务无需启动,其会自己判断/etc/rc.d/rc.local是否有可执行权限以判断要不要启动该服务。
@开机过程用到的主要配置文件
/etc/modules-load.d/*.conf
:单纯要核心加载模块的位置;/etc/modprobe.d/*.conf
:可以加上模块参数的位置,一般是以用户名为单位,对用户的启动环境做设置,如/etc/modprobe.d/study.conf
;/etc/sysconfig/*
:systemd为了兼容于systemV ,其实很多的服务脚本设定还是会读取位于/etc/sysconfig/ 底下的环境配置文件;
2. 内核与内核模块
2.1 核心与核心模块的位置
-
核心:
/boot/vmlinuz
或/boot/vmlinuz-version
; -
核心解压缩所需RAM Disk:
/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/
如果有新硬件,OS并不支持,但有驱动程序,有如下两种操作:
- 重新编译核心,并加入最新的硬件驱动程序原始码;
- 将该硬件的驱动程序编译成为模块,在开机时加载该模块;
2.2 内核模块的相依性
内核模块间的相依性记录在/lib/modules/$(uname -r)/modules.dep
文件当中,建立该文件需要用到depmod指令。
kernel 核心模块扩展名一定是.ko 结尾的,当使用 depmod 之后,该程序会跑到模块标准放置目录/lib/modules/$(uname -r)/kernel , 并依据相关目录的定义将全部的模块读出来分析,最终将分析的结果写入modules.dep 文件中。
2.3 内核模块的观察
2.4 内核模块的加载和移除
模块的加载有两种方式,其一是使用modprobe
指令,modprobe会主动搜寻modules.dep的内容,先克服模块相依性,才决定需要加载的模块;其二是insmod
指令,其完全由使用者自行加载一个完整文件名模块,不会主动分析模块相依性。
模块的移除使用rmmod
命令,也需要自行找到模块的完整文件名,也可使用modprobe -r
指令来进行,范例见上上图。
2.5 内核模块的额外参数设定
参照/etc/modprobe.d/*.conf
的内容,重点是为用户建立拓展名为.conf的文件,通过options将参数加入内核模块。
3. Boot Loader: grub2
grub2 的优点包括有:
- 认识与支持较多的文件系统,并且可以使用grub2 的主程序直接在文件系统中搜寻核心档名;
- 开机的时候,可以自行编辑与修改开机设定项目,类似bash 的指令模式;
- 可以动态搜寻配置文件,而不需要在修改配置文件后重新安装grub2 。亦即是我们只要修改完/boot/grub2/grub.cfg 里头的设定后,下次开机就生效;
3.1 grub2的两个stage
在BIOS读取完硬件信息,接下来要去开机装置的MBR读取boot loader。loader有三个功能,如1.1节中所说,但是MBR大小仅为446bytes,无法容纳loader程序的代码。为解决这一问题,Linux将boot loader的程序代码执行与设定值加载分成两个阶段来执行
- Stage 1:执行boot loader 主程序:
第一阶段为执行boot loader 的主程序,这个主程序必须要被安装在开机区,亦即是MBR 或者是bootsector 。但如前所述,因为MBR 实在太小了,所以,MBR 或boot sector 通常仅安装boot loader 的最小主程序, 并没有安装loader 的相关配置文件; - Stage 2:主程序加载配置文件:
第二阶段为透过boot loader 加载所有配置文件与相关的环境参数文件(包括文件系统定义与主要配置文件grub.cfg), 一般来说,配置文件都在/boot/grub2
底下。loader 读取了这些文件系统定义数据后,就能够认识文件系统并读取在该文件系统内的内核文件。
3.2 grub2的配置文件
3.2.1 /boot/grub2/grub.cfg
@磁盘与分区槽在grub2中的代号
安装在 MBR 的grub2 主程序,最重要的任务之一就是从磁盘当中加载核心文件, 以让核心能够顺利的驱动整个系统的硬件。所以 grub2 必须要认识硬盘才行。其格式为:
- 硬盘代号以小括号( ) 包起来;
- 硬盘以hd 表示,后面会接一组数字;
- 以搜寻顺序做为硬盘的编号!(这个重要!)
- 第一个搜寻到的硬盘为0 号,第二个为1 号,以此类推;
- 每颗硬盘的第一个partition 代号为1 ,依序类推。
@/boot/grub2/grub.cfg配置文件(了解即可,不做更改)
基本上,grub2 不希望使用者修改grub.cfg 这个配置文件,取而代之的是修改几个特定的配置文件之后,由grub2-mkconfig
这个指令来产生新的grub.cfg 文件。
在grub.cfg 最开始的部份,其实大多是环境设定与默认值设定等,比较重要的当然是默认由哪个选项开机(set default
), 以及预设的秒数(set timeout
), 再来则是每一个选单的设定,就是在 menuentry
这个设定值之后的项目 ,其中有两个menuentry设置,即开机时有两个选单可供选择。
menuentry之后会有若干项目规范,然后选单中有三个比较重要的项目:
set root='hd0,gpt2'
:这 root 是指定grub2 配置文件所在的那个装置linux16 /vmlinuz-... root=/dev/mapper/centos-root ...
:是Linux 核心文件以及核心执行时所下达的参数。linux16 后面接的档名得要跟上面的root 搭配在一起,才是完整的绝对路径文件名,root=/file/name 那个root 指的是linux 文件系统中,根目录是在哪个装置上的意思;initrd16 /initramfs-3.10...
:initramfs所在的文件名,需要搭配set root=xxx
项目的装置才能得到正确位置。
3.2.2 /etc/default/grub
由于/boot/grub2/grub.cfg
内容复杂,数据量庞大,所以官方说明不建议手动修改该文件。建议通过/etc/default/grub
主要环境配置文件和/etc/grub.d/
目录中的相关配置文件处理较为妥当。先介绍/etc/default/grub
,该主配置文件的内容如下:
如需自定义开机选单直接编辑/etc/default/grub
这个主要环境配置文件后,(如设置默认开机选项,对于GRUB_DEFAULT
项目使用菜单选项的id进行设置)再以 grub2-mkconfig
来重建 grub.cfg, e.g:grub2-mkconfig -o /boot/grub2/grub.cfg
3.2.3 /etc/grub.d/*(选单建置的脚本)
在使用grub-mkconfig
的过程中,其会去分析/etc/grub.d/*
里面的文件,然后执行该文件来建置grub.cfg。一般来说,该目录下会有这些文件存在:
- 00_header:主要在建立初始的显示项目,包括需要加载的模块分析、屏幕终端机的格式、倒数秒数、选单是否需要隐藏等等,大部分在/etc/default/grub 里面所设定的变量,大概都会在这个脚本当中被利用来重建grub.cfg 。
- 10_linux:根据分析/boot 底下的文件,尝试找到正确的linux 核心与读取这个核心需要的文件系统模块与参数等,都在这个脚本运作后找到并设定到grub.cfg 当中。因为这个脚本会将所有在/boot 底下的每一个核心文件都对应到一个选单,因此核心文件数量越多,你的开机选单项目就越多了。如果未来你不想要旧的核心出现在选单上,那可以透过移除旧核心来处理即可。
- 30_os-prober:这个脚本默认会到系统上找其他的partition 里面可能含有的操作系统,然后将该操作系统做成选单来处理就是了。如果你不想要让其他的操作系统被侦测到并拿来开机,那可以在
/etc/default/grub
里面加上GRUB_DISABLE_OS_PROBER=true
取消这个文件的运作。 - ★40_custom:如果你还有其他想要自己手动加上去的选单项目,或者是其他的需求,那么建议在这里补充即可;
3.2.4 ★menuentry的常用设定:开机选单设置
- 直接指定或自定义内核开机
①如果要linux内核直接开机,直接通过grub2-mkconfig读10_linux脚本直接制作即可;
②若是有特别参数自定义内核开机方式,操作流程为:(1)先到/bootgrub.cfg
当中取得你要制作的那个核心的选单项目,然后将它复制到/etc/grub.d/40_custom
当中;(2)再到/etc/grub.d/40_custom 当中依据需求修改即可; - 通过chainloader方式移交loader控制权
chain loader (开机管理程序的链结) 仅是在将控制权交给下一个boot loader 而已, 所以grub2 并不需要认识与找出kernel 文件,他只是将 boot 的控制权交给下一个boot sector 或MBR 内的boot loader ,所以它也不需要去查验下一个 boot loader 的文件系统。
一般情况下,chainloader设定只需两个,一个是预计要前往的boot sector 所在的分区槽代号, 另一个则是设定 chainloader 在那个分区槽的boot sector (第一个扇区)。
若Windows 分区槽在/dev/sda1 ,且只有一颗硬盘,那么要grub 将控制权交给windows 的loader 只需在/etc/grub.d/40_custom中添加如下menuentry:
如果每次都想要让 windows 变成默认的开机选项, 那么在 /etc/default/grub 当中设定好GRUB_DEFAULT=Windows
(设置时通过 --id内容来处理) 然后再次 grub2-mkconfig 即可。
- ★设置开机选单的过程
- (optional)如果是从其他boot loader 转成grub2 时,得先使用
grub2-install
安装grub2 配置文件; - (optional)承上,如果安装到partition 时,可能需要加上额外的许多参数才能够顺利安装上去(比如xfs文件系统);
- 开始编辑
/etc/default/grub
及/etc/grub.d/*
这几个重要的配置文件; - 使用
grub2-mkconfig -o /boot/grub2/grub.cfg
来建立开机的配置文件;
3.3 建立新的initramfs文件
若想重置或新建initramfs文件,使用dracut
/mkinitrd
指令进行:
3.4 为选单设置密码
grub2的主要控制有:(1)grub2 的选单指令列修改(只能root能够修改)与(2)进入选择的选单开机流程。
建立/etc/grub.d/01_users
文件进行选单的密码设置,在grub2的选单管理中,有两种身份来设定密码,分别时superusers和users:
- superusers: 设定系统管理员与相关参数还有密码等,使用这个密码的用户,将可在grub2 内具有所有修改的权限。但一旦设定了这个superusers 的参数,则所有的指令修改将会被变成受限制的!
- users: 设定一般账号的相关参数与密码,可以设定多个用户。使用这个密码的用户可以选择要进入某些选单项目。不过,选单项目也得要搭配相对的账号才行喔!(一般来说,使用这种密码的账号并不能修改选单的内容,仅能选择进入选单去开机而已)
给与账号选单密码的时候有两种方式:
-
password_pbkdf2 账号『使用grub2-mkpasswd-pbkdf2 所产生的密码』
-
password 账号『没加密的明码』
注意:在选单参数设置中,既没有–user也没有–unrestricted参数,则只能由root才能进入该选单并修改。
4. 忘记root密码的解决
@方法1
- 启动linux主机,进入到开机画面,在开机选单画面上按e进入编辑模式;
- 在linux16内核项目上添加
rd.break
参数,之后按下ctrl+x
开机,进入RAM Disk环境,此时系统挂载在/sysroot
目录下;
对于上面的/.autorelabel
指令,系统在开机就会重新写入SELinux 的type 到每个文件,因此会花不少的时间。还有个节省时间的方法可以处理:
- 在
rd.break
模式下,修改完root 密码后,将/etc/selinux/config
内的SELinux 类型改为permissive - 重新启动后,使用root 的身份下达
restorecon -Rv /etc
仅修改/etc 底下的文件;
-重新修改/etc/selinux/config
改回enforcing ,然后setenforce 1
即可
@方法2(不推荐)
- 启动linux主机,进入到开机画面,在开机选单画面上按e进入编辑模式;
- 在linux16内核项目上添加
init=/bin/bash
参数,开机完成后给我们一个不需要root密码而有root权限的bash; - 重新挂载根目录
- 强制关机