Linux开启动过程详解

Posted by BenderFly on April 2, 2019

Linux启动过程

整个过程基本可以分为POST–>BIOS–>MBR(GRUB)–>Kernel–>Init–>Runlevel。下面会详细说明每个过程的作用。
boot-init

BIOS

  BIOS(Basic Input/Output System),基本输入输出系统,该系统存储于主板的ROM芯片上,计算机在开机时,会最先读取该系统,然后会有一个加电自检过程(按下电源开关后,BIOS检查设备,可以听到“滴”的一声就说明设备正常),这个过程其实就是检查CPU和内存,计算机最基本的组成单元(控制器、运算器和存储器),还会检查其他硬件,若没有异常就开始加载BIOS程序到内存当中。
详细的BIOS功能,这边就不说了,BIOS主要的一个功能就是存储了磁盘的启动顺序,BIOS会按照启动顺序去查找第一个磁盘头的MBR信息,并加载和执行MBR中的Bootloader程序,若第一个磁盘不存在MBR,则会继续查找第二个磁盘(PS:启动顺序可以在BIOS的界面中进行设置),一旦BootLoader程序被检测并加载内存中,BIOS就将控制权交接给了BootLoader程序。
boot-init

MBR

  MBR(Master Boot Record),主引导记录,MBR存储于磁盘的头部(它指的是硬盘的第一个扇区),大小为512bytes,其中,446bytes用于存储BootLoader程序,64bytes用于存储分区表信息(DPT(Disk Partition Table),最后2bytes用于MBR的有效性检查(“55,AA”是分区表结束的标志)。

Boot Loader

Boot Loader是一个很小但非常重要的可执行文件(程序),不同的操作系统都有其对应的boot loadr,如windows 的叫ntldr,linux的就有很多了,有grub grub2,Syslinux等

BIOS怎么知道MBR的地址?如何知道我的硬盘/U盘/光盘上有没有安装操作系统?第一个问题: 有一个硬件中断INT13会告诉BIOS外设的MBR地址。第二个问题:首先,对于windows,之前我们讲到它要求一个主分区设置为活动分区,并把windows操作系统安装在该主分区。这个时候,windows的引导程序ntldr就会寻找到活动分区,并加载系统文件到内存。那要是linux系统呢,它可是个跟洪七公一样的大侠,居无定所,住哪都行的。要处理这样一个棘手的问题,就要隆重请出本文的主角啦——grub2. grub2现在是Ubuntu linux 默认的引导程序(boot loader)。如果你的一块硬盘里有两个操作系统,一个是linux,一个是windows.那么如果是windows的引导程序ntldr安装在了MBR上,除非使用像easyBCD这样的配置工具进行额外配置,它是只能够启动windows的,因为它不会把控制权交到别个系统的boot loader手上。而如果MBR上安装的是grub2,那么它就默认提供3大功能:
A.提供一块硬盘上所有安装的系统的选择菜单;
B.载入操作linux系统内核,移交控制权给内核;
C.将控制权移交给其他系统的boot loader
也就是说在grub2启动,在屏幕上呈现出操作系统的选择菜单,如果你并没有选择linux,而是选择了windows那么grub2就会把控制权交给ntldr,进而启动windows操作系统。

boot loader 最主要的作用就是识别自己的操作系统文件,并将它载入到内存。好问题又来了:一块硬盘只有一个MBR,那么你装上了grub2之后,就不能再装windows的ntldr了,如果没有ntldr就载入不了windows的系统文件,启动不了系统。但现在我们很多人的电脑上都有双系统,那是怎么做到的呢?答案是这样的:大家看上边的图,每个分区都有一个引导扇区(boot sector)。那么当你把系统安装在某个分区时,它同时也会在该分区的boot sector上安装它自己的boot loader。因为一个分区只能装一个系统(注意是分区,不是一块硬盘),这样该分区上的系统就可以被该分区的boot sector上的boot loader引导了,不会有其他系统的loader来抢占这个位置。但windowns除了在自己所在分区的boot sector上安装自己的boot loader,它还会在硬盘的MBR上也装上一份(linux的brub2不会这样)。所以,如果你先安装了linux,再装windows,那么最后MBR上就是ntldr,它是不会将控制权转交给别个系统的loader的,所以你也就启动不了linux。

GRUB

GRUB(Grand Unified Bootloader),多系统启动程序,其执行过程可分为三个步骤:

Stage1:这个其实就是MBR,它的主要工作就是查找并加载第二段Bootloader程序(stage2),但系统在没启动时,MBR根本找不到文件系统,也就找不到stage2所存放的位置,因此,就有了stage1_5
Stage1_5:该步骤就是为了识别文件系统
Stage2:GRUB程序会根据/boot/grub/grub.conf文件查找Kernel的信息,然后开始加载Kernel程序,当Kernel程序被检测并在加载到内存中,GRUB就将控制权交接给了Kernel程序。
PS:实际上这个步骤/boot还没被挂载,GRUB直接识别grub所在磁盘的文件系统,所以实际上应该是/grub/grub.conf文件,该配置文件的信息如下:

    grub.conf:   

    #boot=/dev/sda  

    default=0        #设定默认启动的title的编号,从0开始  

    timeout=5       #等待用户选择的超时时间  

    splashimage=(hd0,0)/boot/grub/splash.xpm.gz    #GRUB的背景图片  

    hiddenmenu     #隐藏菜单  

    title CentOS (2.6.18-194.el5PAE)      #内核标题  

        root (hd0,0)         #内核文件所在的设备  

        kernel /vmlinuz-2.6.18-194.el5PAE ro root=LABEL=/     #内核文件路径以及传递给内核的参数  

        initrd /initrd-2.6.18-194.el5PAE.img                            #ramdisk文件路径  

# Kernel

  Kernel,内核,Kernel是Linux系统最主要的程序,实际上,Kernel的文件很小,只保留了最基本的模块,并以压缩的文件形式存储在硬盘中,当GRUB将Kernel读进内存,内存开始解压缩内核文件。讲内核启动,应该先讲下initrd这个文件,

  initrd(Initial RAM Disk),它在stage2这个步骤就被拷贝到了内存中,这个文件是在安装系统时产生的,是一个临时的根文件系统(rootfs)。 因为Kernel为了精简,只保留了最基本的模块,因此,Kernel上并没有各种硬件的驱动程序,也就无法识rootfs所在的设备,故产生了initrd这个文件,该文件装载了必要的驱动模块,当Kernel启动时,可以从initrd文件中装载驱动模块,直到挂载真正的rootfs,然后将initrd从内存中移除。

  Kernel会以只读方式挂载根文件系统,当根文件系统被挂载后,开始装载第一个进程(用户空间的进程),执行/sbin/init,之后就将控制权交接给了init程序。

Init

  init,初始化,顾名思义,该程序就是进行OS初始化操作,实际上是根据/etc/inittab(定义了系统默认运行级别)设定的动作进行脚本的执行,第一个被执行的脚本为/etc/rc.d/rc.sysinit,这个是真正的OS初始化脚本,简单讲下这个脚本的任务(可以去看看实际脚本,看看都做了什么):

1、激活udev和selinux;
2、根据/etc/sysctl.conf文件,来设定内核参数;
3、设定系统时钟;
4、装载硬盘映射;
5、启用交换分区;
6、设置主机名;
7、根文件系统检测,并以读写方式重新挂载根文件系统;
8、激活RAID和LVM设备;
9、启用磁盘配额;
10、根据/etc/fstab,检查并挂载其他文件系统;
11、清理过期的锁和PID文件

执行完后,根据配置的启动级别,执行对应目录底下的脚本,最后执行/etc/rc.d/rc.local这个脚本,至此,系统启动完成。

  

Runlevel

runlevel,运行级别,不同的级别会启动的服务不一样,init会根据定义的级别去执行相应目录下的脚本,Linux的启动级别分为以下几种
0:关机模式
1:单一用户模式(直接以管理员身份进入)
2:多用户模式(无网络)
3:多用户模式(命令行)
4:保留
5:多用户模式(图形界面)
6:重启

在不同的运行级别下,/etc/rc.d/rc这个脚本会分别执行不同目录下的脚本

Run level 0 – /etc/rc.d/rc0.d/
Run level 1 – /etc/rc.d/rc1.d/
Run level 2 – /etc/rc.d/rc2.d/
Run level 3 – /etc/rc.d/rc3.d/
Run level 4 – /etc/rc.d/rc4.d/
Run level 5 – /etc/rc.d/rc5.d/
Run level 6 – /etc/rc.d/rc6.d/

这些目录下的脚本只有K和S开头的文件,K开头的文件为开机需要执行关闭的服务,S开头的文件为开机需要执行开启的服务。

超详细描述linux系统从开机到登陆界面的启动过程
对于linux系统的初学者来说,理解并掌握linux系统启动流程能够使你够深入的理解linux系统,还可以通过系统的启动过程来分析问题解决问题。

Linux系统的启动流程
 关于linux系统的启动流程可以分为以下步骤:

POST(加电自检)–>加载BIOS(Basic Input/Outpu System)–>确定启动设备(Boot sequence)、加载Boot Loader–>加载内核(kernel)初始化initrd–>运行/sbin/init初始化系统–>打印用户登录提示符

 

下面让我们逐步剖析说明系统启动过程:

1、POST开机自检

 linux开机加电后,系统开始开机自检,该过程主要对计算机各种硬件设备进行检测,如CPU、内存、主板、硬盘、CMOS芯片等,如果出现致命故障则停机,并且由于初始化过程还没完成,所以不会出现任何提示信号;如果出现一般故障则会发出声音等提示信号,等待故障清除;若未出现故障,加电自检完成。

2、加载主引导目录(MBR)

开机自检完成,查找可启动设备,加载主引导目录(MBR) 开机自检完成后,CPU首先读取位于CMOS中的BIOS程序,按照BIOS中设定的启动次序(Boot Sequence)逐一查找可启动设备,找到可启动的设备后,去该设备的第一个扇区中读取MBR,那么MBR是什么哪?它又有什么作用哪?

MBR存在于可启动磁盘的0磁道0扇区,占用512字节,它主要用来告诉计算机从选定的可启动设备的哪个分区来加载引导加载程序(Boot loader),MBR中存在如下内容:

(1) Boot Loader 占用446字节,存储有操作系统(OS)相关信息,如操作系统名称,操作系统内核位置等,它的主要功能是加载内核到内存中运行。

(2) Partition Table 分区表,占用64字节,每个主分区占用16字节(这就是为啥一块硬盘只能有4个主分区啦^_^)

(3)分区表有效性标记占用2字节

CPU将MBR读取至内存,运行GRUB(Boot Loader常用的有GRUB和LILO两种,现在常用的是GRUB),GRUB会把内核加载到内存去执行。
kernel.png

由上图可以看出,内核文件存在于/boot目录下,但是在GRUB加载内核时,连/还没有被加载,它是怎么在磁盘上找到内核的哪?我们来查看一下GRUB的配置文件可以找到答案。
grub-conf.png

在/boot/grub/grub.conf中可以看到 root (hd0,0)这一行实际上是指定了/目录的所在的位置,但这个根并不是真正的根,而是/所在的位置,可以理解成/boot是处在(hd0,0)/boot,而这里的(hd0,0)指的是第一个磁盘的第一个分区,GRUB不是通过文件系统来访问内核的,以因为此时内核还没有启动,不存在文件系统,而是直接访问     第一个磁盘的第一个分区(通过MBR中的分区表来识别分区),而识别MBR中的分区的文件系统,则是由GRUB通过加载自身携带的系统文件来实现的,这些文件在/boot/grub目录中
grub-dir.png

大家可以看到,红色标出的地方就是GRUB可以识别的文件系统的类型了。事实上,总结一下,grub启动过程可以分为两个步骤:

第1阶段   BIOS加载MBR中的GRUB(GRUB第一阶段的文件),而GRUB只有446字节,无法实现太多功能,所以利用该阶段的文件去加载1.5阶段的文件(/boot/grub/下的文件)

第1.5阶段用来加载识别文件系统的文件,识别完系统后才可以找到/boot目录。

第2阶段  寻找内核并加载到内存中。

下图是GRUB启动过程中所用的的文件
grub-dir2.png

3、加载内核,初始化initrd

 GRUB把内核加载到内存后展开并运行,此时GRUB的任务已经完成,接下来内核将会接管并完成 探测硬件–>加载驱动–>挂载根文件系统–>切换至根文件系统(rootfs)–>运行/sbin/init完成系统初始化。但是,问题来了,要访问根文件系统必须要加载根文件系统所在的设备,而这时根文件系统又没有挂载,要挂载根文件系统有需要根文件系统的驱动程序,这是一个典型的先有鸡先有蛋的问题啊!为解决这个问题,GRUB在加载内核同时,也把initrd加载到内存中并运行,那么initr又起到了什么作用哪? boot-dir.png

initrd展开后的文件 initrd-dir.png

linux中/下的文件 root-dir.png

我们可以看到,其实initrd文件其实是一个虚拟的根文件系统,里面有bin、lib、lib64、sys、var、etc、sysroot、dev、proc、tmp等根目录,它的功能就是讲内核与真正的根建立联系,内核通过它加载根文件系统的驱动程序,然后以读写方式挂载根文件系统,至此,内核加载完成。

4、运行/sbin/init,进行系统初始化

内核并加载进内存运行并以读写方式挂载完根文件系统后,执行第一个用户进程init,init首先运行/etc/init/rcS.conf脚本,如下图
init.png

可以看到,init进程通过执行/etc/rc.d/rcS.conf首先调用了/etc/rc.d/rc.sysinit,对系统做初始化设置,我们来看看这个脚本都是做了些什么哪?
rc-sysinit.png

事实上init执行/etc/rc.d/rc.sysinit的初始化将会做很多设置:

1、获得网络环境
2、挂载设备
3、开机启动画面Plymouth(取替了过往的 RHGB)
4、判断是否启用SELinux
5、显示于开机过程中的欢迎画面
6、初始化硬件
7、用户自定义模块的加载
8、配置内核的参数
9、设置主机名
10、同步存储器
11、设备映射器及相关的初始化
12、初始化软件磁盘阵列(RAID)
13、初始化 LVM 的文件系统功能
14、检验磁盘文件系统(fsck)
15、设置磁盘配额(quota)
16、重新以可读写模式挂载系统磁盘
17、更新quota(非必要)
18、启动系统虚拟随机数生成器
19、配置机器(非必要)
20、清除开机过程当中的临时文件
21、创建ICE目录
22、启动交换分区(swap)
23、将开机信息写入/var/log/dmesg文件中

init执行完/etc/rc.d/rc.sysinit后,将会执行/etc/inittab来设定系统运行的默认级别:
inittab.png

如上图,linux中共有[0-6]七个运行级别,而我自己系统的默认运行级别是3. 设定完系统默认运行级别以后,接着调用/etc/rc.d/rc脚本,这个脚本接收默认运行级别参数后,依脚本设置启用或停止/etc/rc.d/rc[0-6].d/中相应的程序,如下图,看一下我系统运行默认级别(级别)3下的内容吧
rc-d-rc.png

如图示,/etc/rc.d/rc[0-6].d/下的文件在系统初始化阶段,以S开头的将被启动,以K开头的将被关闭,文件名K/S 后面的的数字代表优先级,数字越小优先级越高,优先执行。

最后,将执行/etc/rc.d/rc.local脚本,可以根据自己的需求将一些执行命令或者脚本写到其中,当开机时就可以加载。

 # 5、打印登录提示符
 系统初始化完成后,init给出用户登录提示符(login)或者图形化登录界面,用户输入用户和密码登陆后,系统会为用户分配一个用户ID(uid)和组ID(gid),这两个ID是用户的身份标识,用于检测用户运行程序时的身份验证。登录成功后,整个系统启动流程运行完毕!

参考1 参考2 参考3