哈~结束了systemd和GRUB2的了解, 我们就可以更新之前CentOS5&6启动流程啦!
进入系统前
关于从我们摁下电源到BIOS legacy到bootloader的过程, 是没有什么变化的, 所以可以参考前面说过的: BIOS到BootLoader
这次又学习到了一点更细致更具体的, 所以再来谈一谈开机流程这个事情.
传统BIOS开机流程
我们按下主机的电源键, 接着储存在主板上的EEPROM中的BIOS就会开始下面的工作:
初始化
每当我们电脑打开, CPU就会自动重置成初始状态, 准备工作. BIOS boot block(开机区块)初始化阶段开始启动. 因为此时我们的内存啥的都是空的, 没有内容可以执行, 所以主板厂商会让CPU去寻找系统BIOS ROM中的reset vector(重置向量): 也就是用一个固定的位置来启动所谓的BIOS boot program开机程序.
一般来说, 我们的这个程序会出现在内存的0xFFFF的位置, 当然里面其实只有一个jump指令, 进一步真正的BIOS启动程序. 各个BIOS供应商也可以把程序放在不同的位置, 只要通过jump来指定就可以了.
POST(Power On Self Test开机自我检测)
接着BIOS就会开始实行POST, 在过程中检查电脑的各项组件及设定. 然后再物理内存的开头处构建实模式的中断向量表, 这样我们就可以通过特定的中断来寻找硬件了. 接着是BIOS的数据区 以及 中断程序(仅仅是用于实模式的, 在后面加载操作系统的时候就会被抹除.)
这些工作都是硬件层面设计好的 和操作系统没有一点关系.
寻找操作系统
OK~到了这个阶段, BIOS就会根据使用者的设定来决定搜索顺序, 产生INT_19来让CPU执行相应的中断服务程序. 这个程序把启动盘的第一扇区加载到物理内存的0x0700处, 这个也是硬件厂商设定的(因为不知道用户会安装什么操作系统). 这个扇区就是我们的引导程序所在的地方了. 接着就是我们的GRUB来做了. BIOS的使命结束.
UEFI 搭配 GPT
在这里顺便更新一下这种较新的组合是怎么搞得.
先来对比一下, 传统BIOS( BIOS legacy )和UEFI的区别.
比较项目 | 传统BIOS | UEFI |
---|---|---|
使用语言 | 组合语言 | C 语言 |
硬件资源控制 | 使用中断(IRQ)管理不可变的内存存取不可变的输入/输出存取 | 使用驱动程序与协定 |
处理器运行环境 | 16位 | CPU 保护模式 |
扩充方式 | 透过IRQ 连结 | 直接载入驱动程式 |
第三方厂商支援 | 较差 | 较佳且可支持多平台 |
图形化能力 | 较差 | 较佳 |
内建简化作业系统前环境 | 不支持 | 支持 |
可以看出来, 这个UEFI要比传统的BIOS高级很多, 从WikiPedia中摘录一段, 我觉得写的挺清楚:
(UEFI)突破传统16位代码的寻址能力,达到处理器的最大寻址。它利用加载EFI驱动程序的形式,识别及操作硬件,不同于BIOS利用挂载真实模式中断的方式增加硬件功能。后者必须将一段类似于驱动程序的16位代码(如RAID卡的Option ROM)放置在固定的0x000C0000至0x000DFFFF之间存储区中,运行这段代码的初始化部分,它将挂载实模式下约定的中断向量向其他程序提供服务。例如,VGA图形及文本输出中断(INT 10h),磁盘访问中断服务(INT 13h)等等。由于这段存储空间有限(128KB),BIOS对于所需放置的驱动程序代码大小超过空间大小的情况无能为力。
另外,BIOS的硬件服务程序都以16位代码的形式存在,这就给运行于增强模式的操作系统访问其服务造成了困难。因此BIOS提供的服务在现实中只能提供给操作系统引导程序或MS-DOS类操作系统使用。而UEFI系统下的驱动程序可以由EFI Byte Code(EBC)编写而成,EFI Byte Code是一组专用于EFI驱动程序的虚拟机器语言,必须在EFI驱动程序运行环境(Driver Execution Environment,或DXE)下被解释运行。
加上EFI driver开发简单,所有的PC零组件厂商都可以参与,就像现代操作系统的开发模式,这样的模式曾使Windows系统短短几年就变得无比强大. 有了EFI driver,也可以让显卡在开机阶段就载入某种程度的功能,进而可以把传统文字界面为主的BIOS转成图形界面。
至于GPT, 是GUID Partition Table的缩写, 而GUID就是全局唯一标识符的简称. 下面的一张图就把GPT的状态说的很清楚啦.
**LBA是指逻辑区块地址 **
尽管GPT对分区数量没有限制 但是Windows最大就支持128个分区. 至于为什么是128,这是这是因为EFI标准的最小值.
最后补充一个小点, 我们的BIOS设定中由一个secure boot的选项, 这个选项是为了UEFI的安全, 开启后就只会运行经过事先写过的数字签名的验证的程序. 但是这一点遭到了RedHat的反对, 因为大部分的Windows用户的电脑上主板上仅仅写入了OEM和微软的数字签名, 这对Linux的安装就造成了麻烦. 尽管说会提供这个值的修改设置…但是你懂得
进入系统
载入内核和initramfs
虽然这个部分在之前还是说过了, 但是还是再提一遍…因为一些内容是新学到的~
我们通过 Bootloader的管理而开始读取内核的时候, 接下来, 整个内核就会被解压到物理内存中. 接着利用内核的功能来测试周边装置, 也就是各个硬件啦 储存装置,CPU,网卡,声卡等等. 其实这个时候这些硬件的信息已经有了, 这是BIOS扫描得到的. 但是我们的Linux内核是不相信这个结果的, 所以要再次扫描一遍.
有关的文件都放在/boot下了:
1 | [root@WWW ~]# ls --format=single-column -F /boot |
我们知道Linux内核是可以动态加载模块的, 模块在哪里呢? 在我们的/lib/module/$VERSION下面.所以我们也可以自信的说他们在根目录下, 因此为了开机就一定要挂载根目录, 而担心影响到磁盘中的文件系统, 这个根会用只读的方式挂载.
接着又回到了那个难题. 如果你的linux是安装在SATA磁盘上的, 那么通过INT_13取得的Bootloader与kernel来开机, 接着kernel就会开始扫描硬件,尝试挂载根目录. 但是内核无法获得SATA磁盘, 所以需要驱动程序. 但是驱动程序在根目录下, 这就是那个经典的两难问题.
解决方法就是上面的虚拟文件系统. 跟着Bootloader一起载入, 接着在内存中模拟成根目录, 并且提供一堆可执行程序(通常是USB,RAID,LVM啥的驱动程序), 载入模块.
那么这个神奇的initramfs究竟有什么东西呢? 之前我一直傻傻的用cpio在复原, 其实有一个命令专门用来输出这个文件的内容的:
1 | [root@WWW boot]# lsinitrd initramfs-3.10.0-514.21.2.el7.x86_64.img | less |
输出很庞大. 通过观察输出, 我们可以知道这个文件前面是一个文档宣告, 但是后面才是真正用到的部分. 里面有个init程序, 已经给systemd管理了:
1 | lrwxrwxrwx 1 root root 23 Jun 25 08:40 init -> usr/lib/systemd/systemd |
之前说过, systemd的默认target取决于default.target这个软链:
1 | lrwxrwxrwx 1 root root 13 Jun 25 08:40 usr/lib/systemd/system/default.target -> initrd.target |
那么这个initrd.target就是开机启动的第一个target了~ 怎么启动的呢?
1 | initrd.target |
这样, 在内核完整载入完毕之后, 就是我们的systemd的主场了!
进入系统之后
接下来系统连接到/usr/lib/systemd/system这个目录去取按照用户的设定, 读取default.target所指向的目标target(注意呀,刚刚上面说的那个default是由系统生成的根文件系统, 和我们进入系统的那个不是一回事, 所以两个default.target不一样的), 读取完了之后, 就会去下面的两个地方,加载设定
- /etc/systemd/system/XXX(你设置的).target.wants/:使用者设定载入的unit
- /usr/lib/systemd/system/XXX.target.wants/:系统预设载入的unit
一直这样XXX不是个事, 一般都是multi-user吧, 所以就拿这个好了.
我们来看看multi-user.target:
1 | [Unit] |
也就是说, 在必须在basic.target运行完毕之后才能载入…载入什么呢? 我们来看看对应的wants文件夹:
1 | [root@WWW system]# ls /etc/systemd/system/multi-user.target.wants/ |
因为我使用的是Virtual Box来着, 所以可以忽略一些
系统加载的unit有这些:
1 | [root@WWW system]# ls /usr/lib/systemd/system/multi-user.target.wants/ |
简单分析一下systemctl list-dependencies multi-user.target
所输出的依赖服务,基本上我们CentOS 7.x 的systemd 开机流程大约是这样:
- local-fs.target + swap.target:这两个target 主要在挂载本机/etc/fstab 里面所规范的文件系统与相关的内存置换空间
- sysinit.target:这个target 主要在扫描硬件,载入所需要的内核模块等
- basic.target:载入主要的周边硬件驱动程序与防火墙相关任务
- multi-user.target 底下的其它一般系统或网络服务的载入
第一个步骤的那两个target, 很好理解 只要根据fstab就可以了. 那后面呢? 我们可以查看一下他们所启动的服务就知道了.
sysinit.target
基本上,我们可以将这些服务归类成几个大项就是了:
- 特殊文件系统装置的挂载:包括dev-hugepages.mount dev-mqueue.mount 等挂载服务,主要在挂载跟巨量内存分页使用与信息队列的功能。挂载成功后,会在/dev 底下建立/dev/hugepages/, /dev/mqueue/ 等目录;
- 特殊文件系统的启用:包括磁盘阵列(RAID)、网路磁盘(iscsi)、LVM、文件系统对照服务(multipath) 等等,也会在这里被扫描和使用.
- 开机过程的信息传递与动画执行:使用plymouthd 服务搭配plymouth 指令来传递动画与信息
- 登录日志的使用:就是systemd-journald 这个服务的启用.
- 载入额外的内核模块:透过/etc/modules-load.d/*.conf 文件的设定,让内核额外载入所需要的内核模块.
- 载入额外的内核参数设定:包括/etc/sysctl.conf 以及/etc/sysctl.d/*.conf 内部设定
- 启动系统的随机产生器:随机产生器可以帮助系统进行一些密码加密演算的功能
- 设定终端(console)字体
- 启动动态设备管理服务:就是udevd 啦 用在动态对应实际设备存取与设备档名对应的一个服务.
不论你即将使用哪种操作环境来使用系统,这个sysinit.target 几乎都是必要的工作 从上面你也可以看的出来,基本的内核功能、文件系统、文件系统硬件的驱动等等, 都在这个时刻处理完毕~所以,这个sysinit.target 的阶段是挺重要.
basic.target
执行完sysinit.target 之后,再来则是basic.target 这个项目了.
sysinit.target 在初始化系统,而basic.target 的阶段主要启动的服务大概有这些:
- 载入firewalld 防火墙:CentOS 7.x 以后使用firewalld 取代iptables 的防火墙设定,虽然最终都是使用iptables 的架构, 不过在设定上面差很多.
- 载入CPU 的微指令功能;
- 启动与设定SELinux 的安全上下文:如果由disable 的状态改成enable 的状态,或者是管理员设定强制重新设定一次SELinux 的安全上下
- 将目前的开机过程所产生的开机信息写入到/var/log/dmesg 当中
- 由/etc/sysconfig/modules/*.modules 及/etc/rc.modules 载入管理员指定的模块
- 载入systemd 支持的timer 功能;
系统启动完毕
这样就差不多了, 接下来就是关于模块的一些内容 关于这个 和之前的就没有什么不同了, 参考这个地方就可以了: 内核管理