linux开机引导和启动过程(详细)(含配置文件解读)

过程概述

  1. BIOS加电自检
  2. 加载MBR中的Bootloader
  3. 加载内核,初始化initrd
  4. 加载父进程,执行初始化程序
  5. 打开终端

BIOS开机加电自检

BIOS(基本输入输出系统),该系统存储于主板的ROM芯片上,计算机在开机时,会最先读取该系统,然后会有一个加电自检过程。这个过程其实就是检查CPU和内存,计算机最基本的组成单元(控制器、运算器和存储器),还会检查其他硬件,若没有异常就开始加载BIOS程序到内存当中。BIOS的功能大概可以分为3个部分:

  1. 用于电脑刚接通电源时对硬件部分的检测,也叫做加电自检(Power On Self Test,简称POST)。功能是检查电脑是否良好,通常完整的POST自检将包括对CPU,640K基本内存,1M以上的扩展内存,ROM,主板,CMOS存储器,串并口,显示卡,软硬盘子系统及键盘进行测试,一旦在自检中发现问题,系统将给出提示信息或鸣笛警告。自检中如发现有错误,将按两种情况处理:

    • 对于严重故障(致命性故障)则停机,此时由于各种初始化操作还没完成,不能给出任何提示或信号;
    • 对于非严重故障则给出提示或声音报警信号,等待用户处理。
  2. 初始化硬件(这个玩过开发板的应该都很清楚)。包括创建中断向量、设置寄存器、对一些外部设备进行初始化和检测等,其中很重要的一部分是BIOS设置,主要是对硬件设置的一些参数,当电脑启动时会读取这些参数,并和实际硬件设置进行比较,如果不符合,会影响系统的启动。

  3. 引导操作系统程序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(2538925389字节)。该文件通常位于MBR与第一个分区之间,也就是0630\sim63扇区之间(未包括6363),总大小为62512=3174462*512=31744字节。这个文件,存放了一些通用的文件系统驱动程序,如标准的 EXT 和其它的 Linux 文件系统,如 FATNTFS 等。所以这阶段就是负责执行Stage_1.5中存放的驱动程序,并加载相关的驱动程序。

Grub2core.img 远比老一版的 Grub1 更复杂且更强大。这意味着 Grub2core.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,这里有篇文章已经说得很详细,这里就直接与大家分享了:

加载内核并初始化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

每个配置项格式如下:

id:runlevel:action:processid:runlevel:action:process

各项含义如下:

  1. id

    id是指入口标识符,它是一个字符串,对于getty或mingetty等其他login程序项,要求id与tty的编号相同,否则getty程序将不能正常工作。

  2. runlevel

    runlevel是init所处于的运行级别的标识.runlevel可以是并列的多个值,以匹配多个运行级别,对大多数action来说,仅当runlevel与当前运行级别匹配成功才会执行

    • 0:表示关机
    • 1:表示单用户模式,在这个模式中,用户登录不需要密码,默认网卡驱动是不被加载,一些服务不能用。
    • 2:表示多用户模式,NFS服务不开启
    • 3:表示命令行模式
    • 4:这个模式保留未用
    • 5:表示图形用户模式
    • 6:表示重启系统
  3. action

    • respawn:当process终止后马上启动一个新的
    • wait:当进入指定的runlevels后process才会启动一次,并且到离开这个runlevels终止
    • initdefault:设定默认的运行级别,即我们开机之后默认进入的运行级别,不能是0,6,你懂的
    • sysinit:系统初始化,只有系统开机或重新启动的时候,这个process才会被执行一次
    • powerwait:当init接收到电源失败信号的时候执行相应的process,并且如果init有进程在运行,会等待这个进程完成之后,再执行相应的process
    • powerfail:当init接收到电源失败信号的时候执行相应的process,并且如果init有进程在运行,不会等待这个进程完成,它会直接执行相应的process
    • powerokwait:电源已经故障,但是在等待执行对应操作的时候突然来电了就执行对应的process
    • powerfailnow:当电源故障并且init被通知UPS电源已经快耗尽执行相对应的process
    • ctrlaltdel:当用户按下ctrl+alt+del这个组合键的时候执行对应的process
    • boot:只有在引导过程中,才执行该进程,但不等待该进程的结束;当该进程死亡时,也不重新启动该进程
    • bootwait:只有在引导过程中,才执行该进程,并等待进程的结束;当该进程死亡时,也不重新启动该进程
    • off:如果process正在运行,那么就发出一个警告信号,等待20秒后,再通过杀死信号强行终止该process。如果process并不存在那么就忽略该登记项
    • once:启动相应的进程,但不等待该进程结束便继续处理/etc/inittab文件中的下一个登记项;当该进程死亡时,init也不重新启动该进程
  4. 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.targetgraphical.target 的一个符号链接的别名。systemd 总是通过 default.target 启动系统。default.target 绝不应该指向halt.targetpoweroff.targetreboot.target。

Systemd进程在被加载到内后,将依次执行五个目标:

  1. /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
  1. 根据上一步中可以看到default.target强依赖于multi-user.target,所以接下来这步就要执行如下两个目录中的目标了:
  • /etc/systemd/system/multi-user.target.wants/
  • /usr/lib/systemd/system/multi-user.target.wants/

大部分的用户程序的目标都在这两个目录下有配置,在这步就一并启动了

#/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
  1. 接下来到了basic.target目标了,内容如下:
  • /etc/systemd/system/basic.target.wants/
  • /usr/lib/systemd/system/basic.target.wants/
#/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
  1. sysinit.target会启动重要的系统服务例如系统挂载,内存交换空间和设备,内核补充选项.
  • /etc/systemd/system/sysinit.target.wants/
  • /usr/lib/systemd/system/sysinit.target.wants/
[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
  1. 然后是最后一个目标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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章