linuxboot&systemd

启动流程:POST --> Boot Sequence --> Bootloader --> kernel +initramfs(initrd) --> rootfs --> /sbin/init
init: CentOS 5: SysV init
CentOS 6: Upstart
CentOS 7: Systemd
1.Systemd:
系统启动和服务器守护进程管理器,负责在系统启动或运行时,激活系统资源,服务器进程和其它进程
(1)Systemd新特性:
系统引导时实现服务并行启动
按需启动守护进程
自动化的服务依赖关系管理
同时采用socket式与D-Bus总线式激活服务
系统状态快照
(2)核心概念:unit
unit表示不同类型的systemd对象,通过配置文件进行标识和配置;文件中主要包含了系统服务、监听socket、保存的系统快照以及其它与init相关的信息
(3)配置文件:
/usr/lib/systemd/system:
每个服务最主要的启动脚本设置,类似于之前/etc/init.d/
/run/systemd/system:
系统执行过程中所产生的服务脚本,比上面目录优先运行
/etc/systemd/system:
管理员建立的执行脚本,类似于/etc/rc.d/rcN.d/Sxx类的功能,比上面目录优先运行
3.Unit类型
(1)Systemctl –t help 查看unit类型
 Service unit: 文件扩展名为.service, 用于定义系统服务
 Target unit: 文件扩展名为.target,用于模拟实现运行级别
 Device unit: .device, 用于定义内核识别的设备
 Mount unit: .mount, 定义文件系统挂载点
 Socket unit: .socket, 用于标识进程间通信用的socket文件,也可在系统启动时,延迟启动服务,实现按需启动
 Snapshot unit: .snapshot, 管理系统快照
 Swap unit: .swap, 用于标识swap设备
 Automount unit: .automount,文件系统的自动挂载点
 Path unit: .path,用于定义文件系统中的一个文件或目录使用,常用于当文件系统变化时,延迟激活服务,如:spool 目录
(2)特性
关键特性:
基于socket的激活机制:socket与服务程序分离
基于d-bus的激活机制:
基于device的激活机制:
基于path的激活机制:
系统快照:保存各unit的当前状态信息于持久存储设备中
向后兼容sysv init脚本
 不兼容:
systemctl命令固定不变,不可扩展
非由systemd启动的服务,systemctl无法与之通信和控制
4.管理服务
 管理系统服务:
CentOS 7: service unit
注意:能兼容早期的服务脚本
 命令:systemctl COMMAND name.service
 启动:service name start ==> systemctl start
name.service
 停止:service name stop ==> systemctl stop
name.service
 重启:service name restart ==> systemctl restart name.service
 状态:service name status ==> systemctl status
name.service
 条件式重启:已启动才重启,否则不做操作
service name condrestart ==> systemctl tryrestart
name.service
 重载或重启服务:先加载,再启动
systemctl reload-or-restart name.service
 重载或条件式重启服务:
systemctl reload-or-try-restart name.service
 禁止自动和手动启动:
systemctl mask name.service
 取消禁止:
systemctl unmask name.service
5.服务查看
(1)查看某服务当前激活与否的状态:
systemctl is-active name.service
(2)查看所有已经激活的服务:
systemctl list-units --type|-t service
(3)查看所有服务:
systemctl list-units --type service --all|-a
6.chkconfig命令的对应关系:
(1)设定某服务开机自启:
chkconfig name on ==> systemctl enable name.service
(2)设定某服务开机禁止启动:
chkconfig name off ==> systemctl disable name.service
(3)查看所有服务的开机自启状态:
chkconfig --list ==>
systemctl list-unit-files --type service
(4)用来列出该服务在哪些运行级别下启用和禁用
chkconfig sshd –list ==>
ls /etc/systemd/system/*.wants/sshd.service
(5)查看服务是否开机自启:
systemctl is-enabled name.service
(6)查看服务的依赖关系:
systemctl list-dependencies name.service
(7)杀掉进程:
systemctl kill unitname
7.服务状态
(1)systemctl list-unit-files --type service --all显示状态
 loaded:Unit配置文件已处理
 active(running):一次或多次持续处理的运行
 active(exited):成功完成一次性的配置
 active(waiting):运行中,等待一个事件
 inactive:不运行
 enabled:开机启动
 disabled:开机不启动
 static:开机不启动,但可被另一个启用的服务激活
(2)显示所有单元状态
systemctl 或 systemctl list-units
(3)只显示服务单元的状态
systemctl --type=service(–type后面跟服务类型)
(4)显示sshd服务单元
systemctl –l status sshd.service (-l显示更详细的信息)
(5)验证sshd服务当前是否活动
systemctl is-active sshd
(6)启动,停止和重启sshd服务
systemctl start sshd.service
systemctl stop sshd.service
systemctl restart sshd.service
systemctl 命令示例
(7)重新加载配置
systemctl reload sshd.service
(8)列出活动状态的所有服务单元
systemctl list-units --type=service
(9)列出所有服务单元
systemctl list-units --type=service --all
(10)查看服务单元的启用和禁用状态
systemctl list-unit-files --type=service
(11)列出失败的服务
systemctl --failed --type=service

Paste_Image.png
eg:
 列出依赖的单元
systemctl list-dependencies sshd
 验证sshd服务是否开机启动
systemctl is-enabled sshd
 禁用network,使之不能自动启动,但手动可以
systemctl disable network
 启用network
systemctl enable network
 禁用network,使之不能手动或自动启动
systemctl mask network
 启用network
systemctl unmask network
8.运行级别
(1)target units:
unit配置文件:.target
ls /usr/lib/systemd/system/.target
systemctl list-unit-files --type target --all
(2)运行级别:
0 ==> runlevel0.target, poweroff.target
1 ==> runlevel1.target, rescue.target
2 ==> runlevel2.target, multi-user.target
3 ==> runlevel3.target, multi-user.target
4 ==> runlevel4.target, multi-user.target
5 ==> runlevel5.target, graphical.target
6 ==> runlevel6.target, reboot.target
(3)查看依赖性:
systemctl list-dependencies graphical.target
(4) 级别切换:
init N ==> systemctl isolate name.target
systemctl isolate multi-user.target
注:只有/lib/systemd/system/
.target文件中
AllowIsolate=yes 才能切换(修改文件需执行systemctl daemonreload才能生效)
(5)查看target:
runlevel ; who -r
systemctl list-units --type target
(6)获取默认运行级别:
/etc/inittab ==> systemctl get-default
(7)修改默认级别:
/etc/inittab ==>
systemctl set-default name.target
systemctl set-default multi-user.target
ls –l /etc/systemd/system/default.target
(8)切换至紧急救援模式:
systemctl rescue
(9) 切换至emergency模式:
systemctl emergency
(10)其它常用命令:
传统命令init,poweroff,halt,reboot都成为systemctl软链接
关机:systemctl halt、systemctl poweroff
重启:systemctl reboot
挂起:systemctl suspend
休眠:systemctl hibernate
休眠并挂起:systemctl hybrid-sleep
9.CentOS7引导顺序
 UEFi或BIOS初始化,运行POST开机自检
 选择启动设备
 引导装载程序, centos7是grub2
 加载装载程序的配置文件:/etc/grub.d/
/etc/default/grub /boot/grub2/grub.cfg
 加载initramfs驱动模块
 加载内核选项
 内核初始化,centos7使用systemd代替init
 执行initrd.target所有单元,包括挂载/etc/fstab
 从initramfs根文件系统切换到磁盘根目录
 systemd执行默认target配置,配置文件
/etc/systemd/system/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需要的服务
10.service unit文件格式
(1)配置文件
/etc/systemd/system:系统管理员和用户使用
/usr/lib/systemd/system:发行版打包者使用
 以 “#” 开头的行后面的内容会被认为是注释
 相关布尔值
1、yes、on、true 都是开启,
0、no、off、false 都是关闭
 时间单位默认是秒,所以要用毫秒(ms)分钟(m)等须显式说明
(2)service unit file文件通常由三部分组成:
• [Unit]:定义与Unit类型无关的通用选项;用于提供unit描述信息、unit行为及依赖关系等
• [Service]:与特定类型相关的专用选项;此处为Service类型
• [Install]:定义由“systemctl enable”以及"systemctl
disable“命令在实现服务启用或禁用时用到的一些选项

Paste_Image.png
Unit段的常用选项:
 Description:描述信息
 After:定义unit的启动次序,表示当前unit应该晚于哪些unit启动,其功能与Before相反
 Requires:依赖到的其它units,强依赖,被依赖的units无法激活时,当前unit也无法激活
 Wants:依赖到的其它units,弱依赖
 Conflicts:定义units间的冲突关系
Service段的常用选项:
 Type:定义影响ExecStart及相关参数的功能的unit进程启动类型
• simple:默认值,这个daemon主要由ExecStart接的指令串来启动,启动后常驻于内存中
• forking:由ExecStart启动的程序透过spawns延伸出其他子程序来作为此daemon的主要服务。原生父程序在启动结束后就会终止
• oneshot:与simple类似,不过这个程序在工作完毕后就结束了,不会常驻在内存中
• dbus:与simple类似,但这个daemon必须要在取得一个D-Bus的名称后,才会继续运作.因此通常也要同时设定BusNname= 才行
• notify:在启动完成后会发送一个通知消息。还需要配合NotifyAccess 来让 Systemd 接收消息
• idle:与simple类似,要执行这个daemon必须要所有工作都顺利执行完毕后才会执行。这类的daemon通常是开机到最后才执行即可的服务
 EnvironmentFile:环境配置文件
 ExecStart:指明启动unit要运行命令或脚本的绝对路径
 ExecStartPre: ExecStart前运行
 ExecStartPost: ExecStart后运行
 ExecStop:指明停止unit要运行的命令或脚本
 Restart:当设定Restart=1 时,则当次daemon服务意外终止后,会再次自动启动此服务
Install段的常用选项:
• Alias:别名,可使用systemctl command Alias.service
• RequiredBy:被哪些units所依赖,强依赖
• WantedBy:被哪些units所依赖,弱依赖
• Also:安装本服务的时候还要安装别的相关服务
 注意:对于新创建的unit文件,或者修改了的unit文件,要通知systemd重载此配置文件,而后可以选择重启
systemctl daemon-reload
服务Unit文件示例:
 vim /etc/systemd/system/bak.service
[Unit]
Description=backup /etc
Requires=atd.service
[Service]
Type=simple
ExecStart=/bin/bash -c “echo /testdir/bak.sh|at now”
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl start bak
11.设置内核参数
设置内核参数,只影响当次启动
 启动时,在linux16行后添加systemd.unit=desired.target
 systemd.unit=emergency.target
 systemd.unit=rescue.target相当于init1
 rescue.target 比emergency 支持更多的功能,例如日志等
 systemctl default 进入默认target
12.启动排错
(1)文件系统损坏
先尝试自动修复,失败则进入emergency shell,提示用户修复
(2)在/etc/fstab不存在对应的设备和UUID
等一段时间,如不可用,进入emergency shell
(3)在/etc/fstab不存在对应挂载点
systemd 尝试创建挂载点,否则提示进入emergency shell.
(4)在/etc/fstab不正确的挂载选项
提示进入emergency shell
13.破解CentOS7的root口令
方法一:
启动时任意键暂停启动
按e键进入编辑模式
将光标移动linux16开始的行,添加内核参数rd.break
按ctrl-x启动
 mount –o remount,rw /sysroot(改文件权限)
 chroot /sysroot
 passwd root
 touch /.autorelabel(这是给文件加标签)
 exit
 reboot
方法二
 启动时任意键暂停启动
 按e键进入编辑模式
 将光标移动linux16开始的行,改为rw init=/sysroot/bin/sh
 按ctrl-x启动
 chroot /sysroot
 passwd root
 touch /.autorelabel
 exit
 reboot
14.修复GRUB2
GRUB“the Grand Unified Bootloader”
引导提示时可以使用命令行界面
可从文件系统引导
 主要配置文件 /boot/grub2/grub.cfg
 修复配置文件
grub2-mkconfig > /boot/grub2/grub.cfg
15.修复grub
grub2-install /dev/sda BIOS环境
grub2-install UEFI环境
 调整默认启动内核
vim /etc/default/grub
GRUB_DEFAULT=0

Systemd的基本概念

System单元的概念
系统初始化需要做的事情非常多。需要启动后台服务,比如启动 SSHD 服务;需要做配置工作,比如挂载文件系统。这个过程中的每一步都被 systemd 抽象为一个配置单元,即 unit。可以认为一个服务是一个配置单元;一个挂载点是一个配置单元;一个交换分区的配置是一个配置单元;等等。systemd 将配置单元归纳为以下一些不同的类型
单元的常见类型
service unit : 文件扩展名为.service,用于定义系统类服务
target unit : 文件扩展为.target,用于实现模拟"运行级别"
device unit : 文件扩展为.device,用于定义内核识别的设备。
mount unit : 文件扩展为.mount,定义文件系统挂载点,利用logind服务,为用户的会话进程分配CGroup资源
socket unit : 文件扩展为.socket,用于标识进程间通信的socket文件
snapshot unit : 文件扩展为.snapshot,管理系统快照
swap unit : 文件扩展为.swap,用于标识swap设备
automount unit : 文件扩展为.automount,文件系统自动挂载设备
path unit : 文件扩展为.path,用于定义文件系统中的一个文件或目录
timer unit : 文件扩展为.timer,定时器配置单元,用来定时触发用户定义的操作,这类配置单元取代了atd、crond等传统的定时服务
Target和运行级别的对应关系
sysvinit和systemd目标的对应表
sysvinit systemd target 备注
0 poweroff.target 关闭系统
1,s,single rescue.target 单用户模式
2,4 multi-user.target 用户定义/域特定运行级别。默认等同于 3
3 multi-user.target 多用户,非图形化界面
5 graphical.target 多用户,图形化界面
6 reboot.target 重启
emergency emergency.target 紧急shell
Systemd的并发启动原理
并发启动原理之一:解决socket依赖
绝大多数的服务依赖是套接字依赖。比如服务 A 通过一个套接字端口 S1 提供自己的服务,其他的服务如果需要服务 A,则需要连接 S1。因此如果服务 A 尚未启动,S1 就不存在,其他的服务就会得到启动错误。所以传统地,人们需要先启动服务 A,等待它进入就绪状态,再启动其他需要它的服务。Systemd 认为,只要我们预先把 S1 建立好,那么其他所有的服务就可以同时启动而无需等待服务 A 来创建 S1 了。如果服务 A 尚未启动,那么其他进程向 S1 发送的服务请求实际上会被 Linux 操作系统缓存,其他进程会在这个请求的地方等待。一旦服务 A 启动就绪,就可以立即处理缓存的请求,一切都开始正常运行。
那么服务如何使用由 init 进程创建的套接字呢?
Linux 操作系统有一个特性,当进程调用 fork 或者 exec 创建子进程之后,所有在父进程中被打开的文件句柄 (file descriptor) 都被子进程所继承。套接字也是一种文件句柄,进程 A 可以创建一个套接字,此后当进程 A 调用 exec 启动一个新的子进程时,只要确保该套接字的 close_on_exec 标志位被清空,那么新的子进程就可以继承这个套接字。子进程看到的套接字和父进程创建的套接字是同一个系统套接字,就仿佛这个套接字是子进程自己创建的一样,没有任何区别。
这个特性以前被一个叫做 inetd 的系统服务所利用。Inetd 进程会负责监控一些常用套接字端口,比如 Telnet,当该端口有连接请求时,inetd 才启动 telnetd 进程,并把有连接的套接字传递给新的 telnetd 进程进行处理。这样,当系统没有 telnet 客户端连接时,就不需要启动 telnetd 进程。Inetd 可以代理很多的网络服务,这样就可以节约很多的系统负载和内存资源,只有当有真正的连接请求时才启动相应服务,并把套接字传递给相应的服务进程。
和 inetd 类似,systemd 是所有其他进程的父进程,它可以先建立所有需要的套接字,然后在调用 exec 的时候将该套接字传递给新的服务进程,而新进程直接使用该套接字进行服务即可。
解决D-bus依赖
D-Bus 是 desktop-bus 的简称,是一个低延迟、低开销、高可用性的进程间通信机制。它越来越多地用于应用程序之间通信,也用于应用程序和操作系统内核之间的通信。很多现代的服务进程都使用D-Bus 取代套接字作为进程间通信机制,对外提供服务。比如简化 Linux 网络配置的 NetworkManager 服务就使用 D-Bus 和其他的应用程序或者服务进行交互:邮件客户端软件 evolution 可以通过 D-Bus 从 NetworkManager 服务获取网络状态的改变,以便做出相应的处理。
D-Bus 支持所谓"bus activation"功能。如果服务 A 需要使用服务 B 的 D-Bus 服务,而服务 B 并没有运行,则 D-Bus 可以在服务 A 请求服务 B 的 D-Bus 时自动启动服务 B。而服务 A 发出的请求会被 D-Bus 缓存,服务 A 会等待服务 B 启动就绪。利用这个特性,依赖 D-Bus 的服务就可以实现并行启动。
解决文件系统依赖
系统启动过程中,文件系统相关的活动是最耗时的,比如挂载文件系统,对文件系统进行磁盘检查(fsck),磁盘配额检查等都是非常耗时的操作。在等待这些工作完成的同时,系统处于空闲状态。那些想使用文件系统的服务似乎必须等待文件系统初始化完成才可以启动。但是 systemd 发现这种依赖也是可以避免的。
Systemd 参考了 autofs 的设计思路,使得依赖文件系统的服务和文件系统本身初始化两者可以并发工作。autofs 可以监测到某个文件系统挂载点真正被访问到的时候才触发挂载操作,这是通过内核 automounter 模块的支持而实现的。比如一个 open()系统调用作用在"/misc/cd/file1"的时候,/misc/cd 尚未执行挂载操作,此时 open()调用被挂起等待,Linux 内核通知 autofs,autofs 执行挂载。这时候,控制权返回给 open()系统调用,并正常打开文件。
Systemd 集成了 autofs 的实现,对于系统中的挂载点,比如/home,当系统启动的时候,systemd 为其创建一个临时的自动挂载点。在这个时刻/home 真正的挂载设备尚未启动好,真正的挂载操作还没有执行,文件系统检测也还没有完成。可是那些依赖该目录的进程已经可以并发启动,他们的 open()操作被内建在 systemd 中的 autofs 捕获,将该 open()调用挂起(可中断睡眠状态)。然后等待真正的挂载操作完成,文件系统检测也完成后,systemd 将该自动挂载点替换为真正的挂载点,并让 open()调用返回。由此,实现了那些依赖于文件系统的服务和文件系统本身同时并发启动。
当然对于"/"根目录的依赖实际上一定还是要串行执行,因为 systemd 自己也存放在/之下,必须等待系统根目录挂载检查好。
不过对于类似/home 等挂载点,这种并发可以提高系统的启动速度,尤其是当/home 是远程的 NFS 节点,或者是加密盘等,需要耗费较长的时间才可以准备就绪的情况下,因为并发启动,这段时间内,系统并不是完全无事可做,而是可以利用这段空余时间做更多的启动进程的事情,总的来说就缩短了系统启动时间。
服务的循环依赖
Systemd 能保证事务完整性。Systemd 的事务概念和数据库中的有所不同,主要是为了保证多个依赖的配置单元之间没有环形引用,存在循环依赖,那么 systemd 将无法启动任意一个服务。此时 systemd 将会尝试解决这个问题,因为配置单元之间的依赖关系有两种:required 是强依赖;want 则是弱依赖,systemd 将去掉 wants 关键字指定的依赖看看是否能打破循环。如果无法修复,systemd 会报错。Systemd 能够自动检测和修复这类配置错误,极大地减轻了管理员的排错负担
基于path激活机制
判断一个文件在不在, 如果在可以立即激活一个进程或服务
SysVinit与systemd管理命令对比
项目|sysvinit | systemd | 备注|
---------|----------------|----------|
启动|service NAME start | systemctl start NAME.service|
停止|service NAME stop | systemctl stop NAME.service|
重启|service NAME restart| systemctl restart NAME.service|
状态查看|service NAME status|systemctl status NAME.service|
条件式重启|service NAME condrestart |systemctl condrestart NAME.service|如果服务运行就重启
重载或重启服务|—|systemctl reload-or-restart NAME.service|如果支持reload就reload,不然就重启|
重载或条件式重启服务|----|systemctl reload-or-restart NAME.service|
查看某服务当前激活与否的状态|—|systemctl is-active NAME.service|
查看所有已激活的服务|chkconfig --list | systemctl list-units -t service|
查看所有服务(已激活及未激活)|–|systemctl list-units -t service -a|
设置服务开机自启| chkconfig NAME on | systemctl enable NAME.service|
禁止服务开机自启| chkconfig NAME off | systemctl disable NAME.service|
查看某服务是否能开机启动| chkconfig --list NAME | systemctl is-enabled NAME.service|
禁止某服务设定为开机自启|—|systemctl mask NAME.service|
取消禁止某服务开机自启|—| systemctl unmask NAME.service|
查看服务的依赖关系|—| systemctl list-dependencies NAME.service| |
修改默认运行级别|修改/etc/inittab文件| systemctl set-default NAME.target|将/usr/lib/systemd/system/的文件连接至/etc/systemd/system/目录中|
切换系统运行级别| init RUNLEVEL | systemctl isolate NAME.target|
查看目前的运行级别|runlevel,who -r| systemctl get-default |
systemd电源管理命令
命令 操作
systemctl reboot 重启机器
systemctl poweroff 关机
systemctl suspend 挂起
systemctl hibernate 休眠
systemctl hybrid 混合休眠模式(快照并挂起)
service unit file的配置说明:
文件通常由三部分组成:
[Unit] : 定义与unit类型无关的通用选项,用于提供当前unit的描述信息,unit行为及依赖关系等

[Install] :定义由systemctl enable或systemctl disable命令在实现服务启用或禁用时用到的选项
unit段的常用选项:
Description: 描述信息;意义性描述
After : 定义unit的启动次序,表示当前unit应该晚于那些unit启动,其功能与before相反
Requies : 依赖到的其它units,强依赖,被依赖的units无法激活时,当前unit即无法激活
wants: 指明依赖到的其它units,弱依赖,依赖的不激活,也能激活自己,
Conflicts: 定义units间的冲突关系
service段的常用选项:
type:用于定义影响execstart及相关参数的功能的unit进程启动类型
simple: 由execstart启动的命令为主进程
forking: 由execstart启动的命令,其中一个子进程会成为主进程,父进程会退出
onehot: 功能类simple
dubs
notify: 类似于simple
idle :
environmentfile:启动时环境配置文件,为execstart提供变量
execstart :指明启动unit要运行的命令或脚本,execstartpre ,execstartpost
execreload
execstop: 指明停止unit要运行的命令或脚本
restart :
Install 段的常用选项
alias
requireby: 被哪些units所依赖
wantsby : 被那些units所依赖
注意:对于新创建的unit文件或修改了unit文件,要通知systemd重载配置文件
systemctl daemon-reload
systemd的相关配置文件路径
/usr/lib/systemd/system
/run/systemd/system
/etc/systemd/system

事实上,操作系统的启动分为两个阶段:引导boot和启动startup。引导阶段开始于打开电源开关,结束于内核初始化完成和 systemd 进程成功运行。启动阶段接管了剩余工作,直到操作系统进入可操作状态。
总体来说,Linux 的开机引导和启动过程是相当容易理解,下文将分节对于不同步骤进行详细说明。
BIOS 上电自检(POST)
引导装载程序 (GRUB2)
内核初始化
启动 systemd,其是所有进程之父。
注意,本文以 GRUB2 和 systemd 为载体讲述操作系统的开机引导和启动过程,是因为这二者是目前主流的 linux 发行版本所使用的引导装载程序和初始化软件。当然另外一些过去使用的相关软件仍然在一些 Linux 发行版本中使用。
引导过程
引导过程能以两种方式之一初始化。其一,如果系统处于关机状态,那么打开电源按钮将开启系统引导过程。其二,如果操作系统已经运行在一个本地用户(该用户可以是 root 或其他非特权用户),那么用户可以借助图形界面或命令行界面通过编程方式发起一个重启操作,从而触发系统引导过程。重启包括了一个关机和重新开始的操作。
BIOS 上电自检(POST)
上电自检过程中其实 Linux 没有什么也没做,上电自检主要由硬件的部分来完成,这对于所有操作系统都一样。当电脑接通电源,电脑开始执行 BIOS(基本输入输出系统Basic I/O System)的 POST(上电自检Power On Self Test)过程。
在 1981 年,IBM 设计的第一台个人电脑中,BIOS 被设计为用来初始化硬件组件。POST 作为 BIOS 的组成部分,用于检验电脑硬件基本功能是否正常。如果 POST 失败,那么这个电脑就不能使用,引导过程也将就此中断。
BIOS 上电自检确认硬件的基本功能正常,然后产生一个 BIOS 中断 INT 13H,该中断指向某个接入的可引导设备的引导扇区。它所找到的包含有效的引导记录的第一个引导扇区将被装载到内存中,并且控制权也将从引导扇区转移到此段代码。
引导扇区是引导加载器真正的第一阶段。大多数 Linux 发行版本使用的引导加载器有三种:GRUB、GRUB2 和 LILO。GRUB2 是最新的,也是相对于其他老的同类程序使用最广泛的。
GRUB2
GRUB2 全称是 GRand Unified BootLoader,Version 2(第二版大一统引导装载程序)。它是目前流行的大部分 Linux 发行版本的主要引导加载程序。GRUB2 是一个用于计算机寻找操作系统内核并加载其到内存的智能程序。由于 GRUB 这个单词比 GRUB2 更易于书写和阅读,在下文中,除特殊指明以外,GRUB 将代指 GRUB2。
GRUB 被设计为兼容操作系统多重引导规范,它能够用来引导不同版本的 Linux 和其他的开源操作系统;它还能链式加载专有操作系统的引导记录。
GRUB 允许用户从任何给定的 Linux 发行版本的几个不同内核中选择一个进行引导。这个特性使得操作系统,在因为关键软件不兼容或其它某些原因升级失败时,具备引导到先前版本的内核的能力。GRUB 能够通过文件 /boot/grub/grub.conf
进行配置。(LCTT 译注:此处指 GRUB1)
GRUB1 现在已经逐步被弃用,在大多数现代发行版上它已经被 GRUB2 所替换,GRUB2 是在 GRUB1 的基础上重写完成。基于 Red Hat 的发行版大约是在 Fedora 15 和 CentOS/RHEL 7 时升级到 GRUB2 的。GRUB2 提供了与 GRUB1 同样的引导功能,但是 GRUB2 也是一个类似主框架(mainframe)系统上的基于命令行的前置操作系统(Pre-OS)环境,使得在预引导阶段配置更为方便和易操作。GRUB2 通过 /boot/grub2/grub.cfg
进行配置。
两个 GRUB 的最主要作用都是将内核加载到内存并运行。两个版本的 GRUB 的基本工作方式一致,其主要阶段也保持相同,都可分为 3 个阶段。在本文将以 GRUB2 为例进行讨论其工作过程。GRUB 或 GRUB2 的配置,以及 GRUB2 的命令使用均超过本文范围,不会在文中进行介绍。
虽然 GRUB2 并未在其三个引导阶段中正式使用这些阶段stage名词,但是为了讨论方便,我们在本文中使用它们。
阶段 1
如上文 POST(上电自检)阶段提到的,在 POST 阶段结束时,BIOS 将查找在接入的磁盘中查找引导记录,其通常位于 MBR(主引导记录Master Boot Record),它加载它找到的第一个引导记录中到内存中,并开始执行此代码。引导代码(及阶段 1 代码)必须非常小,因为它必须连同分区表放到硬盘的第一个 512 字节的扇区中。 在传统的常规 MBR 中,引导代码实际所占用的空间大小为 446 字节。这个阶段 1 的 446 字节的文件通常被叫做引导镜像(boot.img),其中不包含设备的分区信息,分区是一般单独添加到引导记录中。
由于引导记录必须非常的小,它不可能非常智能,且不能理解文件系统结构。因此阶段 1 的唯一功能就是定位并加载阶段 1.5 的代码。为了完成此任务,阶段 1.5 的代码必须位于引导记录与设备第一个分区之间的位置。在加载阶段 1.5 代码进入内存后,控制权将由阶段 1 转移到阶段 1.5。
阶段 1.5
如上所述,阶段 1.5 的代码必须位于引导记录与设备第一个分区之间的位置。该空间由于历史上的技术原因而空闲。第一个分区的开始位置在扇区 63 和 MBR(扇区 0)之间遗留下 62 个 512 字节的扇区(共 31744 字节),该区域用于存储阶段 1.5 的代码镜像 core.img 文件。该文件大小为 25389 字节,故此区域有足够大小的空间用来存储 core.img。
因为有更大的存储空间用于阶段 1.5,且该空间足够容纳一些通用的文件系统驱动程序,如标准的 EXT 和其它的 Linux 文件系统,如 FAT 和 NTFS 等。GRUB2 的 core.img 远比更老的 GRUB1 阶段 1.5 更复杂且更强大。这意味着 GRUB2 的阶段 2 能够放在标准的 EXT 文件系统内,但是不能放在逻辑卷内。故阶段 2 的文件可以存放于 /boot
文件系统中,一般在 /boot/grub2
目录下。
注意 /boot
目录必须放在一个 GRUB 所支持的文件系统(并不是所有的文件系统均可)。阶段 1.5 的功能是开始执行存放阶段 2 文件的 /boot
文件系统的驱动程序,并加载相关的驱动程序。
阶段 2
GRUB 阶段 2 所有的文件都已存放于 /boot/grub2
目录及其几个子目录之下。该阶段没有一个类似于阶段 1 与阶段 1.5 的镜像文件。相应地,该阶段主要需要从 /boot/grub2/i386-pc
目录下加载一些内核运行时模块。
GRUB 阶段 2 的主要功能是定位和加载 Linux 内核到内存中,并转移控制权到内核。内核的相关文件位于 /boot
目录下,这些内核文件可以通过其文件名进行识别,其文件名均带有前缀 vmlinuz。你可以列出 /boot
目录中的内容来查看操作系统中当前已经安装的内核。
GRUB2 跟 GRUB1 类似,支持从 Linux 内核选择之一引导启动。Red Hat 包管理器(DNF)支持保留多个内核版本,以防最新版本内核发生问题而无法启动时,可以恢复老版本的内核。默认情况下,GRUB 提供了一个已安装内核的预引导菜单,其中包括问题诊断菜单(recuse)以及恢复菜单(如果配置已经设置恢复镜像)。
阶段 2 加载选定的内核到内存中,并转移控制权到内核代码。
内核
内核文件都是以一种自解压的压缩格式存储以节省空间,它与一个初始化的内存映像和存储设备映射表都存储于 /boot
目录之下。
在选定的内核加载到内存中并开始执行后,在其进行任何工作之前,内核文件首先必须从压缩格式解压自身。一旦内核自解压完成,则加载 systemd 进程(其是老式 System V 系统的 init 程序的替代品),并转移控制权到 systemd。
这就是引导过程的结束。此刻,Linux 内核和 systemd 处于运行状态,但是由于没有其他任何程序在执行,故其不能执行任何有关用户的功能性任务。
启动过程
启动过程紧随引导过程之后,启动过程使 Linux 系统进入可操作状态,并能够执行用户功能性任务。
systemd
systemd 是所有进程的父进程。它负责将 Linux 主机带到一个用户可操作状态(可以执行功能任务)。systemd 的一些功能远较旧式 init 程序更丰富,可以管理运行中的 Linux 主机的许多方面,包括挂载文件系统,以及开启和管理 Linux 主机的系统服务等。但是 systemd 的任何与系统启动过程无关的功能均不在此文的讨论范围。
首先,systemd 挂载在 /etc/fstab
中配置的文件系统,包括内存交换文件或分区。据此,systemd 必须能够访问位于 /etc
目录下的配置文件,包括它自己的。systemd 借助其配置文件 /etc/systemd/system/default.target
决定 Linux 系统应该启动达到哪个状态(或目标态target)。default.target
是一个真实的 target 文件的符号链接。对于桌面系统,其链接到 graphical.target
,该文件相当于旧式 systemV init 方式的 runlevel 5。对于一个服务器操作系统来说,default.target
更多是默认链接到 multi-user.target
, 相当于 systemV 系统的 runlevel 3。 emergency.target
相当于单用户模式。
(LCTT 译注:“target” 是 systemd 新引入的概念,目前尚未发现有官方的准确译名,考虑到其作用和使用的上下文环境,我们认为翻译为“目标态”比较贴切。以及,“unit” 是指 systemd 中服务和目标态等各个对象/文件,在此依照语境译作“单元”。)
注意,所有的目标态target和服务service均是 systemd 的单元unit。
如下表 1 是 systemd 启动的目标态target和老版 systemV init 启动运行级别runlevel的对比。这个 systemd 目标态别名 是为了 systemd 向前兼容 systemV 而提供。这个目标态别名允许系统管理员(包括我自己)用 systemV 命令(例如 init 3
)改变运行级别。当然,该 systemV 命令是被转发到 systemd 进行解释和执行的。
每个目标态target有一个在其配置文件中描述的依赖集,systemd 需要首先启动其所需依赖,这些依赖服务是 Linux 主机运行在特定的功能级别所要求的服务。当配置文件中所有的依赖服务都加载并运行后,即说明系统运行于该目标级别。
systemd 也会查看老式的 systemV init 目录中是否存在相关启动文件,若存在,则 systemd 根据这些配置文件的内容启动对应的服务。在 Fedora 系统中,过时的网络服务就是通过该方式启动的一个实例。
如下图 1 是直接从 bootup 的 man 页面拷贝而来。它展示了在 systemd 启动过程中一般的事件序列和确保成功的启动的基本的顺序要求。
sysinit.target 和 basic.target 目标态可以被视作启动过程中的状态检查点。尽管 systemd 的设计初衷是并行启动系统服务,但是部分服务或功能目标态是其它服务或目标态的启动的前提。系统将暂停于检查点直到其所要求的服务和目标态都满足为止。
sysinit.target 状态的到达是以其所依赖的所有资源模块都正常启动为前提的,所有其它的单元,如文件系统挂载、交换文件设置、设备管理器的启动、随机数生成器种子设置、低级别系统服务初始化、加解密服务启动(如果一个或者多个文件系统加密的话)等都必须完成,但是在 sysinit.target 中这些服务与模块是可以并行启动的。
sysinit.target 启动所有的低级别服务和系统初具功能所需的单元,这些都是进入下一阶段 basic.target 的必要前提。

图1:systemd 的启动流程
在 sysinit.target的条件满足以后,systemd 接下来启动 basic.target
,启动其所要求的所有单元。 basic.target
通过启动下一目标态所需的单元而提供了更多的功能,这包括各种可执行文件的目录路径、通信 sockets,以及定时器等。
最后,用户级目标态(multi-user.target
或 graphical.target
) 可以初始化了,应该注意的是 multi-user.target
必须在满足图形化目标态 graphical.target
的依赖项之前先达成。
图 1 中,以 *
开头的目标态是通用的启动状态。当到达其中的某一目标态,则说明系统已经启动完成了。如果 multi-user.target
是默认的目标态,则成功启动的系统将以命令行登录界面呈现于用户。如果 graphical.target
是默认的目标态,则成功启动的系统将以图形登录界面呈现于用户,界面的具体样式将根据系统所配置的显示管理器而定。
故障讨论
最近我需要改变一台使用 GRUB2 的 Linux 电脑的默认引导内核。我发现一些 GRUB2 的命令在我的系统上不能用,也可能是我使用方法不正确。至今,我仍然不知道是何原因导致,此问题需要进一步探究。
grub2-set-default
命令没能在配置文件 /etc/default/grub
中成功地设置默认内核索引,以至于期望的替代内核并没有被引导启动。故在该配置文件中我手动更改 GRUB_DEFAULT=saved
为 GRUB_DEFAULT=2
,2 是我需要引导的安装好的内核文件的索引。然后我执行命令 grub2-mkconfig > /boot/grub2/grub.cfg
创建了新的 GRUB 配置文件,该方法如预期的规避了问题,并成功引导了替代的内核。
结论
GRUB2、systemd 初始化系统是大多数现代 Linux 发行版引导和启动的关键组件。尽管在实际中,systemd 的使用还存在一些争议,但是 GRUB2 与 systemd 可以密切地配合先加载内核,然后启动一个业务系统所需要的系统服务

猜你喜欢

转载自blog.csdn.net/seaship/article/details/86232953