过程概述
BIOS
加电自检- 加载
MBR
中的Bootloader
- 加载内核,初始化initrd
- 加载父进程,执行初始化程序
- 打开终端
BIOS开机加电自检
BIOS(基本输入输出系统
),该系统存储于主板的ROM
芯片上,计算机在开机时,会最先读取该系统,然后会有一个加电自检过程。这个过程其实就是检查CPU和内存,计算机最基本的组成单元(控制器、运算器和存储器),还会检查其他硬件,若没有异常就开始加载BIOS程序到内存当中。BIOS的功能大概可以分为3个部分:
-
用于电脑刚接通电源时对硬件部分的检测,也叫做加电自检(
Power On Self Test
,简称POST
)。功能是检查电脑是否良好,通常完整的POST自检将包括对CPU,640K基本内存,1M以上的扩展内存,ROM,主板,CMOS存储器,串并口,显示卡,软硬盘子系统及键盘进行测试,一旦在自检中发现问题,系统将给出提示信息或鸣笛警告。自检中如发现有错误,将按两种情况处理:- 对于严重故障(致命性故障)则停机,此时由于各种初始化操作还没完成,不能给出任何提示或信号;
- 对于非严重故障则给出提示或声音报警信号,等待用户处理。
-
初始化硬件(这个玩过开发板的应该都很清楚)。包括创建中断向量、设置寄存器、对一些外部设备进行初始化和检测等,其中很重要的一部分是BIOS设置,主要是对硬件设置的一些参数,当电脑启动时会读取这些参数,并和实际硬件设置进行比较,如果不符合,会影响系统的启动。
-
引导操作系统程序。
BIOS
会按照硬盘的引导记录去查找第一个磁盘头的MBR信息,并加载和执行MBR
中的Bootloader
程序,若第一个磁盘不存在MBR,则会继续查找第二个磁盘(启动顺序可以在BIOS
的界面中进行设置),一旦BootLoader
程序被检测并加载内存中,BIOS
就将控制权交接给了BootLoader
程序(控制权交接其实就是指CPU跳转到Bootloader
程序的内存位置执行)
加载MBR中的BootLoader
MBR(Master Boot Record
)主引导记录,MBR存储于磁盘的头部,大小为512 bytes
,组成为:[446 bytes
(存储BootLoader
程序)+64 bytes
(存储分区表信息)+2 bytes
(用于MBR
的有效性检查)]。
这个部分就需要分为3个阶段来执行了:
Stage_1
BIOS加载它找到的第一个引导记录(MBR)中到内存中,并开始执行此代码。Bootloader
必须非常小,因为它必须连同分区表放到硬盘的第一个 512 字节的扇区中。 在传统的常规 MBR 中,引导代码实际所占用的空间大小为 446
字节。这个MBR中这个 446
字节的文件通常被叫做引导镜像(boot.img)
,其中不包含设备的分区信息,分区是一般单独添加到引导记录中。
Stage_1.5
由于boot.img
必须做的非常小,所以基本不可能有太多复杂的功能,他唯一能做的就是加载Stage_1.5中的存储的代码,也就是core.img
(
25389
25389
25389字节)。该文件通常位于MBR与第一个分区之间,也就是
0
∼
63
0\sim63
0∼63扇区之间(未包括
63
63
63),总大小为
62
∗
512
=
31744
62*512=31744
62∗512=31744字节。这个文件,存放了一些通用的文件系统驱动程序,如标准的 EXT
和其它的 Linux
文件系统,如 FAT
和 NTFS
等。所以这阶段就是负责执行Stage_1.5
中存放的驱动程序,并加载相关的驱动程序。
Grub2
的core.img
远比老一版的Grub1
更复杂且更强大。这意味着Grub2
的core.img
能够放在标准的EXT
文件系统内,但是不能放在逻辑卷内。故在Grub2
中,Stage_1.5
中的文件可以存放于/boot
文件系统中,一般在/boot/grub2
目录下。
Stage_2
进入Stage_2
时,所有的文件(内核、initrd
等)都已存放于/boot
目录及其几个子目录之下。同时,该阶段还可以从 /boot/grub2/i386-pc
目录下加载一些内核运行时模块。所以Stage_2
的主要功能是定位和加载 Linux 内核和initrd
到内存中,并转移控制权。
关于Grub2
在linux
启动时按下C
即可进入Grub2
的命令行模式,这里你可以自己手动引导内核和initrd的加载。关于手动引导的相关操作,可以自行百度。这里我们重点来看下配置文件/boot/grub2/grub.cfg
,这里有篇文章已经说得很详细,这里就直接与大家分享了:
- GRUB2配置文件"grub.cfg"详解
- grub2基础教程-修订版(谷歌文档)
加载内核并初始化initrd
由于Linux内核是很精简的,只保留必要的驱动程序,而现实中的设备种类繁多,例如:根文件系统可以存储在包括IDE、SCSI、USB在内的多种介质上,如果将这些设备的驱动都编译进内核那该多么臃肿,所以就干脆将设备特有的驱动与内核分离,单独形成initrd
(一般自己编译的系统不需要,发行版需要兼容所以会需要)。需要哪种驱动就提取到initrd
中,待启动时提前加载即可。所以内核启动一般分两步:
初始化initrd
在 linux
内核启动前, bootloader
会将存储介质中的 initrd
文件加载到内存,内核启动时会在访问真正的根文件系统前先访问该内存中的 initrd
文件系统,这个过程将会完成相关驱动模块的加载,最主要就是加载根文件系统存储介质的驱动模块等,这样内核就可以正常识别设备和真正的根文件系统了。
initrd( boot loader initialized RAM disk),就是由
bootloader
初始化的内存盘,算是一个很mini版的文件系统。
启动内核
之后,内核会以只读方式挂载根文件系统,当根文件系统被挂载后,开始装载第一个进程(用户空间的进程),执行/sbin/init
,之后就将控制权交接给了init程序。如果是使用systemd的话,则是加载systemd
进程到内存中。
初始化系统
这一节呢,我们分为两个init
系统来叙述:
Sysvinit系统
sysvinit
就是 System V
风格的 init
系统,顾名思义,它源于 System V
系列的 UNIX
。最初的 linux
发行版几乎都是采用 sysvinit
作为 init 系统。sysvinit
用术语 runlevel 来定义 “预订的运行模式”。比如
- runlevel 3 是命令行模式,
- runlevel 5 是图形界面模式,
- runlevel 0 是关机,
- runlevel 6 是重启。
这个初始化系统的涉及到的配置文件如下(按init进程读先后顺序):
/etc/inittab
####省去多余注释#######
###表示当前缺省运行级别为5(initdefault),即默认以图形界面启动
id:5:initdefault:
###启动时自动执行/etc/rc.d/rc.sysinit脚本(sysinit)
# 这一项表示在系统启动时执行/etc/rc.d/rc.sysinit程序
si::sysinit:/etc/rc.d/rc.sysinit
l0:0:wait:/etc/rc.d/rc 0
l1:1:wait:/etc/rc.d/rc 1
l2:2:wait:/etc/rc.d/rc 2
# 当切换到runlevel3(命令行模式时),执行一下/etc/rc.d/rc这个程序,并传递参数3
l3:3:wait:/etc/rc.d/rc 3
l4:4:wait:/etc/rc.d/rc 4
###当运行级别为5时,以5为参数运行/etc/rc.d/rc脚本,init将等待其返回(wait)
l5:5:wait:/etc/rc.d/rc 5
#这个就是同时匹配多个runlevel
l6:23456:wait:/etc/rc.d/rc 6
每个配置项格式如下:
i d : r u n l e v e l : a c t i o n : p r o c e s s id:runlevel:action:process id:runlevel:action:process
各项含义如下:
-
id
id是指入口标识符,它是一个字符串,对于getty或mingetty等其他login程序项,要求id与tty的编号相同,否则getty程序将不能正常工作。
-
runlevel
runlevel是init所处于的运行级别的标识.runlevel可以是并列的多个值,以匹配多个运行级别,对大多数action来说,仅当runlevel与当前运行级别匹配成功才会执行
- 0:表示关机
- 1:表示单用户模式,在这个模式中,用户登录不需要密码,默认网卡驱动是不被加载,一些服务不能用。
- 2:表示多用户模式,NFS服务不开启
- 3:表示命令行模式
- 4:这个模式保留未用
- 5:表示图形用户模式
- 6:表示重启系统
-
action
respawn
:当process终止后马上启动一个新的wait
:当进入指定的runlevels后process才会启动一次,并且到离开这个runlevels终止initdefault
:设定默认的运行级别,即我们开机之后默认进入的运行级别,不能是0,6,你懂的sysinit
:系统初始化,只有系统开机或重新启动的时候,这个process才会被执行一次powerwait
:当init接收到电源失败信号的时候执行相应的process,并且如果init有进程在运行,会等待这个进程完成之后,再执行相应的processpowerfail
:当init接收到电源失败信号的时候执行相应的process,并且如果init有进程在运行,不会等待这个进程完成,它会直接执行相应的processpowerokwait
:电源已经故障,但是在等待执行对应操作的时候突然来电了就执行对应的processpowerfailnow
:当电源故障并且init被通知UPS电源已经快耗尽执行相对应的processctrlaltdel
:当用户按下ctrl+alt+del这个组合键的时候执行对应的processboot
:只有在引导过程中,才执行该进程,但不等待该进程的结束;当该进程死亡时,也不重新启动该进程bootwait
:只有在引导过程中,才执行该进程,并等待进程的结束;当该进程死亡时,也不重新启动该进程off
:如果process正在运行,那么就发出一个警告信号,等待20秒后,再通过杀死信号强行终止该process。如果process并不存在那么就忽略该登记项once
:启动相应的进程,但不等待该进程结束便继续处理/etc/inittab文件中的下一个登记项;当该进程死亡时,init也不重新启动该进程
-
process
表示启动哪个程序或脚本或执行哪个命令
/etc/rc.d/rc.sysinit
所有的运行级别下,init依赖/etc/rc.d/rc.sysinit
这个脚本对系统进行初始化。按照下面的顺序按部就班的初始化系统:
- 激活 udev 和 selinux
- 设置定义在
/etc/sysctl.conf
中的内核参数 - 设置系统时钟
- 加载 keymaps
- 启用交换分区
- 设置主机名(hostname)
- 根分区检查和 remount
- 激活 RAID 和 LVM 设备
- 开启磁盘配额
- 检查并挂载所有文件系统
- 清除过期的 locks 和 PID 文件
- 最后找到指定 runlevel 下的脚本并执行,其实就是启动服务。
/etc/rc.d/rc
这个程序就是根据传入的参数,调用/etc/rc.d/rc0.d~rc6.d
中的相应的脚本程序,来完成相应的初始化工作和启动相应的服务,最后调用/etc/rc.d/rc.local
,这个文件里面可以放一些用户自定义的启动脚本等等
Systemd系统
systemd 是 linux 系统中最新的初始化系统(init),它主要的设计目标是克服 sysvinit
固有的缺点,提高系统的启动速度。
在开始看Systemd
进程做的事情之前,请先了解一下开机启动脚本的编写和选项含义,这里直接推荐一篇不错的文章:编写systemd下服务脚本。新旧系统运行等级映射关系如下:
SystemV 运行级别 | systemd 目标态 | systemd 目标态别名 | 描述 |
---|---|---|---|
halt.target | 停止系统运行但不切断电源。 | ||
0 | poweroff.target | runlevel0.target | 停止系统运行并切断电源. |
S | emergency.target | 单用户模式,没有服务进程运行,文件系统也没挂载。这是一个最基本的运行级别,仅在主控制台上提供一个 shell 用于用户与系统进行交互。 | |
1 | rescue.target | runlevel1.target | 挂载了文件系统,仅运行了最基本的服务进程的基本系统,并在主控制台启动了一个 shell 访问入口用于诊断。 |
2 | runlevel2.target | 多用户,没有挂载 NFS 文件系统,但是所有的非图形界面的服务进程已经运行。 | |
3 | multi-user.target | runlevel3.target | 所有服务都已运行,但只支持命令行接口访问。 |
4 | runlevel4.target | 未使用。 | |
5 | graphical.target | runlevel5.target | 多用户,且支持图形界面接口。 |
6 | reboot.target | runlevel6.target | 重启。 |
default.target | 这个目标态target是总是 multi-user.target 或 graphical.target 的一个符号链接的别名。systemd 总是通过 default.target 启动系统。default.target 绝不应该指向halt.target 、poweroff.target 或 reboot .target。 |
Systemd
进程在被加载到内后,将依次执行五个目标:
/usr/lib/systemd/system/default.target
,这个文件链接到当前目录的graphical.target
[Unit]
Description=Graphical Interface
Documentation=man:systemd.special(7)
Requires=multi-user.target
Wants=display-manager.service
Conflicts=rescue.service rescue.target
After=multi-user.target rescue.service rescue.target display-manager.service
AllowIsolate=yes
- 根据上一步中可以看到
default.target
强依赖于multi-user.target
,所以接下来这步就要执行如下两个目录中的目标了:
大部分的用户程序的目标都在这两个目录下有配置,在这步就一并启动了
#/usr/lib/systemd/system/multi-user.target.wants/multi-user.target
[Unit]
Description=Multi-User System
Documentation=man:systemd.special(7)
Requires=basic.target
Conflicts=rescue.service rescue.target
After=basic.target rescue.service rescue.target
AllowIsolate=yes
- 接下来到了
basic.target
目标了,内容如下:
#/usr/lib/systemd/system/multi-user.target.wants/basic.target
[Unit]
Description=Basic System
Documentation=man:systemd.special(7)
Requires=sysinit.target
After=sysinit.target
Wants=sockets.target timers.target paths.target slices.target
After=sockets.target paths.target slices.target
sysinit.target
会启动重要的系统服务例如系统挂载,内存交换空间和设备,内核补充选项.
[Unit]
Description=System Initialization
Documentation=man:systemd.special(7)
Conflicts=emergency.service emergency.target
Wants=local-fs.target swap.target
After=local-fs.target swap.target emergency.service emergency.target
- 然后是最后一个目标
local-fs.target
,它不会启动用户相关服务,它只处理底层核心服务,这个target会根据/etc/fstab
来执行相关磁盘挂载操作
Systemd系统服务配置修改
/usr/lib/systemd/system/xx服务.service
:官方默认配置文件,不建议修改这个位置的文件,而应该像下面这项这样操作;/etc/systemd/system/xx服务.service.d/custom.conf
:在/etc/systemd/system
下面创建与配置文件相同文件名的目录,但是要加上.d
的扩展名。然后在该目录下创建配置文件即可。另外,配置文件最好附文件名取名为.conf
。 在这个目录下的文件配置会合并到/usr/lib/systemd/system/xx服务.service
。/etc/systemd/system/xx服务.service.wants/*
:此目录内的文件为链接文件,设置相依服务的链接。意思是启动了 xx服务.service 之后,最好再加上这目录下面建议的服务。/etc/systemd/system/xx服务.service.requires/*
:此目录内的文件为链接文件,设置相依服务的链接。意思是在启动 xx服务.service 之前,需要事先启动哪些服务的意思。
打开终端
sysvinit系统
在sysvinit系统中,由于/etc/inittab
中的配置如下:
1:2345:respawn:/sbin/mingetty tty1
2:2345:respawn:/sbin/mingetty tty2
3:2345:respawn:/sbin/mingetty tty3
4:2345:respawn:/sbin/mingetty tty4
5:2345:respawn:/sbin/mingetty tty5
6:2345:respawn:/sbin/mingetty tty6
# 只有我们进入5运行级别,会打开图形用户终端,并且一旦终端终止,就会再创建一个新的
x:5:respawn:/etc/X11/prefdm -nodaemon
在2345运行级别下,会调用/sbin/mingetty
启用6个命令行界面的终端,并且一旦终端终止,就会再创建一个新的。
Systemd系统
在上面初始化的第1步中等待了display-manager.service
这个服务就是用于启动终端界面的,内容如下:
[Service]
ExecStart=/usr/sbin/gdm
ExecStartPost=-/bin/bash -c "TERM=linux /usr/bin/clear > /dev/tty1"
KillMode=mixed
Restart=always
IgnoreSIGPIPE=no
BusName=org.gnome.DisplayManager
StandardOutput=syslog
StandardError=inherit
EnvironmentFile=-/etc/locale.conf
ExecReload=/bin/kill -SIGHUP $MAINPID
[Install]
Alias=display-manager.service