0%

【译】Linux启动过程分析

原文

一直想写一篇Linux启动过程的文章,看到这篇讲得不错,于是打算翻译出来。水平有限,不足之处欢迎指正。原文在此:https://www.crybit.com/linux-boot-process/

前言

你曾经想了解过系统启动的流程吗?做一个系统管理员,了解机器的启动每一个步骤和流程事非常重要的。如果你是一个系统管理员,你应该对启动流程有更好的了解。这篇文章将一步一步讲解Linux的启动流程。我向你保证,你读完这篇文章之后,你会对linux的启动流程有更好的了解。我把Linux启动流程分成了5步:
image
从加电到登录界面,我们可以大致把Linux启动流程分成五部分:BIOS, Boot Loader第一阶段,BootLoader第二阶段,内核和初始化,这些都是启动过程的重要流程,让我们从Linux的BIOS阶段开始吧。

第一步 BIOS

在这篇文章里,我把BIOS(而不是UEFI)作为Linux启动过程分析的第一步。UEFI和BIOS都是启动操作系统之前的低层软件,UEFI是更现代化的方案,支持更大的硬盘,更快的启动时间,更安全的特性和更方便的图形鼠标。至于BIOS,这个一个预先安装在主板上的固件,BIOS控制计算机硬件。BIOS在启动过程中的主要工作是硬件初始化,最主要的工作是POST(Power On Self Test,硬件自检)。POST测试主要是检查一些硬件的读写是否正常。POST 主要不是用来做硬件诊断,它只是检查系统硬件是否存在并且加载一个BootLoader。POST过程中主要有两类错误:1.致命错误,主要是硬件原因;2.非致命错误,主要是软件原因。

POST的主要职责

  1. 检查CPU寄存器
  2. 检查BIOS代码的完整性
  3. 检查基本组件如DMA,计时器,中断控制器
  4. 搜寻,确定系统主存大小
  5. 初始化BIOS
  6. 识别,组织,选择出哪些设备是可以启动的

POST之后会有Beep声表示POST检查结果。一声简短的beep声意味着系统是OK的。两声beep则表示检查失败,错误码会显示在屏幕上。想看更多详细信息参考POST手册。BIOS工作在CPU和IO设备之间,因此他总是能知道计算机的所有硬件信息。如果任何的硬盘或IO设备发生变化,只需更新BIOS即可。BIOS被存储在RRPROM/FLASH内存中,BIOS不能存储在硬盘或者其他设备中,因为BIOS是管理这些设备的。BIOS使用汇编语言编写。

image

请记住,POST是BIOS启动阶段的主要事情,接下来我们把目光转移到boot loader的第一阶段,MBR阶段

第二步 boot load第一阶段(MBR)

Master Boot Record, 是boot loader启动的起始位置。MBR是磁盘上第一个扇区,占512字节。MBR包含程序代码和分区表信息,请看下方图片:
image
当我们给硬盘分区时,每个分区的第一个扇区或者数据单元总是保留下来存放一些启动代码,硬盘的第一个分区则叫MBR,也是因为一样的原因保留下来,比如机械硬盘的1扇区,0柱面,0磁道。
image
MBR的前446字节是主要的boot loader包含可执行代码和错误信息,接下来的64字节包含四个分区表信息,如上图所示,P1表示分区1,P2表示分区2,等等。
4 * 16 bytes = 64 bytes,最后两个字节就是众所周知的魔法数(0xAA55),主要用来检查MBR的合法性。

当我们从硬盘开始启动时,BIOS开始加载并执行boot loader里面的代码,MBR容量太小,有时候没法执行完整的boot loader代码,所以启动要转移到另外的阶段进行。这些阶段在不同的boot loader上不一样,是时候转移到boot loader的第二阶段了。

第三步 boot loader第二阶段

也叫内核加载阶段,这个阶段的主要任务是加载Linux 内核。
有不同类型的 boot loader

  • LILO: Linux Loader
  • GRUB: Grand Unified Boot Loader
    我们这里主要用GRUB,因为LILO有些缺点。GRUB的一个好处是它能识别Linux文件系统,GRUB能从格式为ext2/ext3的文件系统中加载内核。

就像我们之前提到,boot loader代码执行在不同的阶段,因为MBR容量限制。当前阶段执行的代码因GRUB版本不同而不同。下面是主要的阶段:

  1. 阶段1和阶段2:这两个阶段是必要的
  2. 阶段1.5: 可选的

阶段1是一个启动Linux机器的必要必要镜像。通常这部分代码会被嵌入到MBR中,或者启动分区的第一个扇区。这个镜像大小不能超过512字节,因为这是MBR的限制。在阶段1,没法识别文件系统,阶段1从磁盘中加载阶段1.5或者阶段2,此时能识别Linux文件系统细节。
image

阶段2是GRUB的主要镜像。通常你能在文件系统中找到这个镜像。阶段1.5则是连接阶段1和阶段2的桥梁,阶段1.5会被安装到MBR的右边。

Boot Loaders主要是从阶段2开始加载,所以你需要知道Linux boot loader的一些细节。通用的Linux中的Boot Loader主要有:

  1. LILO (Linux Loader)
  2. GRUB (Grand Unified Boot Loader)

现在几乎所有的Linux发行版本都用GRUB作为boot loader. 最新的GRUB版本为 GRUB v2.

LILO和GRUB都能作为MBR上的首要boot loader,或者第二个boot loadeer(分区上),都用来支持引导Linux、FreeBSD、NetBSD这样的操作系统,也可以引导Windows。
LILO配置文件存放在/etc/lilo.conf,Demo如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
boot=/dev/hda
map=/boot/map
install=/boot/boot.b
prompt
timeout=50
message=/boot/message
lba32
default=linux

image=/boot/vmlinuz-2.4.0-0.43.6
label=linux
initrd=/boot/initrd-2.4.0-0.43.6.img
read-only
root=/dev/hda5

other=/dev/hda1
label=dos

通常像GRUB这样的boot loader配置文件也放在etc目录下。
GRUB有两个版本,v1和v2.

GRUB v1

GRUB v1存放在/etc/grub.conf,目录下,它其实事/boot/grub/grub.conf的一个软链接。Demo如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# grub.conf generated by anaconda
#
# Note that you do not have to rerun grub after making changes to this file
# NOTICE: You have a /boot partition. This means that
# all kernel and initrd paths are relative to /boot/, eg.
# root (hd0,0)
# kernel /vmlinuz-version ro root=/dev/sda3
# initrd /initrd-[generic-]version.img
#boot=/dev/sda
default=0
timeout=5
splashimage=(hd0,0)/grub/splash.xpm.gz
hiddenmenu
title CloudLinux Server (2.6.32-673.26.1.lve1.4.27.el6.x86_64)
root (hd0,0)
kernel /vmlinuz-2.6.32-673.26.1.lve1.4.27.el6.x86_64 ro root=UUID=15f2bf27-2e16-4b6f-bc86-fa74314aa8d5 rd_NO_LUKS rd_NO_LVM LANG=en_US.UTF-8 rd_NO_MD SYSFONT=latarcyrheb-sun16 crashkernel=auto KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM rhgb quiet nohz=off
initrd /initramfs-2.6.32-673.26.1.lve1.4.27.el6.x86_64.img

GRUB v2

grub v2存放在/etc/grub2.cfg,它其实是/boot/grub2/grub.cfg的软链接。
v2和v1主要的不同是,我们不能修改配置文件和其他配置。然而,你可以使用不同的grub2-*命令来修改GRUB配置。
一个v2的Demo如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
terminal_output console
if [ x$feature_timeout_style = xy ] ; then
set timeout_style=menu
set timeout=5
# Fallback normal timeout code in case the timeout_style feature is
# unavailable.
else
set timeout=5
fi
### END /etc/grub.d/00_header ###
BEGIN /etc/grub.d/10_linux ###
menuentry 'CentOS Linux (3.10.0-123.4.2.el7.x86_64) 7 (Core)' --class centos --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-3.10.0-123.el7.x86_64-advanced-fe0109f2-6f34-48ae-b51e-1f5fa78305b5' {
load_video
set gfxpayload=keep
insmod gzio
insmod part_msdos
insmod ext2
set root='hd0,msdos1'

以”menuentry”开始的行来定义内核。这比v1要复杂一些,但是我们可以从命令行修改它。我们会在另外一篇文章讨论GRUB配置。

不要忘记我们现在在Linux启动过程的第二阶段,在阶段二,会把GRUB从一个已知的位置加载进来(/boot/grub),同时在读GRUB配置文件和展示启动入口之前,会把其他必要的驱动和内核模块加载进来。下图就是熟悉的GRUB配置页面,你可以从下方选择加载不同的内核。
image

第四步 内核阶段

终于到了内核阶段,内核镜像是压缩的,我们可以从GRUB入口选择不同的内核,如果没有选择,GRUB会自动选择默认的那个去加载,我们当然也可以修改默认配置。

你选择的内核会先加载到内存中,一个镜像文件会包含基本的根文件系统以及内核的所有模块,之后都会被加载到内存中。这个镜像文件的位置是/boot,也即我们所知道的initramfs.

initramfs,是”initial RAM file sytem”的简写,它的前身是initrd “initial ramdisk”. 这个镜像文件包含原始的文件系统。GRUB加载内核的时候会告诉内核这个镜像文件的内存地址,然后内核会挂载这个镜像文件到内存中作为Linux的根文件系统。

之后内核会开始检测系统硬件。启动流程开始根据配置启动INIT(SYSTEMD)进程和对应的DAEMONS进程。这会在下一个阶段完成。

第五步 INIT

内核一但加载到第四步,会自动执行sbin(sbin/init)下面的init程序。[在 RHEL/CENTOS 7中,/sbin/init被链接到 ../lib/systemd/systemd下],当init进程启动,它就成为了你Linux机器的第一个进程。

init进程做的第一件事是读取配置文件, /etc/inittab,这会指引init进程去读初始化配置脚本去配置环境、设置PATH,开启swapping,检查文件系统等等。从/etc/inittab里,系统会根据rc文件开启对应的服务。

这只是一篇Linux启动过程的介绍,关于这个话题,有很多东西都可以继续展开。

接下来会有更详细的文章介绍具体每一步骤,希望你能享受启动过程。

参考资料