开机启动流程分析

本节索引centos


  在对系统启动流程进行分析的时候,我想你必定是对系统有了必定的了解。系统的启动目前来说大都为串行接力的方式来启动。而所谓的并行方式的启动方式也是某一个阶段的并行。因此我按照系统启动的顺序来把文章连缀起来。
app

    *  BIOS阶段
ide

    *  BootLoader阶段
工具

    *  内核阶段学习

    *  用户层阶段
this

wKioL1nCF_bT9vfTAADFVGrEFD8663.png


BIOS阶段
spa


加载BIOS
操作系统

  当按下开机键后,系统会自动自动加载BIOS,加载的详细过程再也不详述,感兴趣的读者可学习微机原理和或对汇编代码分析命令行

BIOS从CMOS芯片中读取硬件配置信息debug

  开机所须要的BIOS设置和用户自定义的设置都保存在CMOS芯片中。

POST自检

  POSTpower on system  test

  BIOS的代码中包含有诊断功能,用以保证重要硬件被正确初始化,好比内存,CPU,以及一些主板上的各类芯片组。若是硬件损坏,用户可经过主板的debug显示或者蜂鸣器的声音来诊断哪些硬件有故障。

加载Bootloader

  POST自检无误后,BIOS会执行一个 INT 13的中断例程来进入加载Bootloader阶段。

  INT13 中断会从第一启动项的0磁道1扇区读取BootLoader,把系统启动的任务移交给BootLoader

Bootloader阶段


Bootloader具体到Linux系统就是grub了。早期使用的LILO以再也不使用。

BootLoader的主要做用就是识别和加载操做系统的内核文件,并移交至内存中运行,进而启动操做系统。

磁盘的第一块扇区被称为主引导记录(MBR—Master boot Record ),该扇区的446字节存储grub第一阶段的代码。把一个庞大的操做系统内核交给446个字节来引导显然是不现实的,因此能够简单理解为第一阶段的grub的任务就是为了加载第二阶段的grub

第二阶段的grub把对资源的控制权转交给内核镜像。

BootLoader主要功能

  1. 提供菜单功能让用户选择操做系统。

  2. 加载内核文件 。

  3. 移交给其余的BootLoader(该项有些bootloader不支持,好比Windowsloader)。

  4. 命令行编辑 当系统BootLoader 加载内核出现故障,用户可在命令行模式下修改启动参数。

 

grub为例分析BootLoader阶段多作的那些事:

  1.  BIOS 进行POST自检并没有误后,执行INT 13中断,读取对应MBR中前446字节的grub stag1代码并执行。

  2. 加载boot分区文件系统驱动。Grubstag1阶段存储了grub的预启动信息和grub stag1_5阶段代码地址信息,grub stag1_5 阶段的代码位于磁盘的主引导记录以后的27个扇区中(修复grub时的数据,非标准,该段空间主要存储挂载/boot分区文件系统所须要的驱动程序。通常在系统的/boot/grub目录下会有stag1_5阶段的备份文件。

  3.  挂载/boot目录,进入grubstag2阶段,读取grub.conf配置文件,根据配置信息,启动或者由用户选择内核启动或者移交给其余其余BootLoader,并提供命令行功能用于手动加载内核。把全部的控制权移交给内核。

注:此阶段的不含有rootfs的概念,寻找内核文件都是以boot为根

Grub.conf文件以下:

default=0

timeout=5

title CentOS 6 (2.6.32-696.el6.x86_64)

         root  (hd0,0)

         kernel   /vmlinuz-2.6.32-696.el6.x86_64 ro  root=/dev/mapper/vg_centos6-lv_root   

            #以boot为根的内核路径

         initrd   /initramfs-2.6.32-696.el6.x86_64.img

 

内核阶段


  Linux内核的设计风格是单内核,单内核设计要支持市面大部分硬件设备的前提下就要把不少的驱动程序编译在内核文件中,会使得内核文件体积异常庞大。可是Linux系统内核在设计上吸取了微内核的一些优势,把大部分的驱动程序,文件系统驱动程序还有一些外围的驱动程序封装成一个个单独的模块,在使用过程当中只需动态的加载所须要的模块就行了。

  内核模块文件在/lib/modules/`uname -r` 这个目录下,这时候问题就来了,若是内核文件要加载模块驱动,就要先从根分区查找模块位置,而要找到根分区,就要有根文件系统的驱动模块,而模块又在根文件系统下又回到了先有鸡先有蛋的问题。

  为了解决这个问题,一种方法是能够把根文件系统的文件系统驱动程序直接编译在内核文件中,可是市场上的Linux发行版本要支持不少文件系统类型,把全部的文件系统驱动程序编译进内核又会让内核文件体积变得很是庞大。为了解决这一矛盾,就有了initrd这个文件,固然这个是最先期的,在centos6版本称为initramfs也称为虚拟文件系统,该文件是在系统安装的最后阶段生成的,只用来加载根文件系统的驱动程序,在内核没法驱动根文件系统的时候就要加载虚拟文件系统,而后在根文件系统下找到对应的驱动模块后在进行根切换。 

系统启动内核阶段的步骤

  1.  加载内核至内存后解压运行,尝试挂载根文件系统,若是挂载成功,动态加载个驱动模块,对周围硬件设备进行探测并进行初始化。

  2.  若是没法挂载根文件系统,加载initrd虚拟文件系统,而后挂载真正的根文件系统,并切换根,再执行第一步。

至此,Linux内核已经创建起来了。

用户层阶段


  内核被加载成功以后,这个系统已经正常运行,这也是最初的Linux,可是做为一个用户是没法直接使用内核的。因此就要初始化启动相关的进程或者服务来供用户来使用。

  管理进程的工具多种,以centOS为例,有sysVinitsystemd两种。本文主要分析sysVinit的启动方式。

Systemd是由sysVint进化而来,所以更好的掌握了sysVinit启动方式有助于理解systemd的启动方式

执行init程序

  内核在引导完成以后会执行系统的第一个进程init。这时也就正式进入了sysVinit的引导环境。Init以后的全部进程都是由init派生出来,它的PID永远为1

init进程加载inittab配置文件

  init进程依据inittab文件来设定运行级别,不一样的运行级别能够定义一组不一样服务的启动顺序。CentOS56版本默认有7个运行级别。详情以下: 

#  Default runlevel. The runlevels used are:

#   0 - halt (Do NOT set initdefault to this)

#   1 - Single user mode

#   2 - Multiuser, without NFS (The same as 3,  if you do not have networking)

#   3 - Full multiuser mode

#   4 - unused

#   5 - X11

#   6 - reboot (Do NOT set initdefault to  this)

#

id:3:initdefault:     #默认的运行级别

  生产环境中默认使用3级别,运行级别的只是用于定义一组不一样的启动顺序,用户彻底能够自定义,也能够自定义这7种运行级别。

执行rc.sysinit

  init在获得运行级别以后并不会当即执行该运行级别的一组服务程序,而执行的第一个程序是/etc/rc.d/rc.sysinit脚本程序。该程序的在centos5inittab文件中有以下一行:

si:sysinit:/etc/rc.d/rc.sysinit

这个脚本所作的工做有不少,包括主机名,文件系统,swapSELinuxudev,内核参数,系统时钟,RaidLVM等服务的开启。为后续服务启动准备基础环境。

加载rc$runlevel.d下的服务

  在rc.sysinit脚本初始化完成以后,会加载默认启动级别下的一组服务。这个时候能够查看对应rc$runlevel.d下的文件,有大量以KS开头的脚本文件(软链接),跟踪rc.d目录下的rc脚本能够发现这样两个循环结构,仔细研究以后符合如下逻辑 

for i  in /etc/rc$runlevel.d/K* ; do

     $i stop        #依次关闭以K开头的服务

done

for i  in /etc/rc$runlevel.d/S* ; do

      $i   start  #依次开启以S开头的服务,即开机自启动

Done

  经过这个结构咱们很容易发现服务的启动时顺序执行的,前一个服务脚本没有执行成功的后续脚本就要等待。

  经过对rc脚本的分析能够得出对应runlevel下以S开头的脚本是开机启动的,咱们能够经过chkconfig命令调整程序是否开启启动或者指定模式下的开机自启动。

跟踪对应模式下的脚本软链接能够,找到服务脚本大多放置在/etc/init.d目录之下。可使用service 命令来管理这些脚本。

  在2345运行级别所对应的rc$runlevel.d目录中都有一个$99local的脚本软链接,该脚本通常是最后一个执行的脚本,该脚本位置在/etc/rc.d/local。因此用户能够把开机后须要执行的操做写在该脚本中。

  固然也能够把开机须要的操做定义为服务放置于/etc/init.d目录下,这时须要修改服务脚本的格式。

相关文章
相关标签/搜索