Centos6系统启动加载流程

in 互联网技术 with 0 comment  访问: 3,983 次

了解一个系统的启动过程,对于一位系统管理员 and 运维是非常重要的。了解系统启动方式对于在系统出现故障时进行有效的故障排除非常重要。当系统启动并在几分钟后知道我们到了登录提示阶段。我们是否试图找出启动序列的所有阶段已经正常通过,以及系统启动期间这些场景背后发生了什么。下面我们就来熟悉一下Centos6系统的启动流程。

整体流程大概如下:
15418429688311.jpg

第一阶段硬件引导

15417778730976.jpg
系统供电开机后,主板BIOS(Basic Input / Output System)运行POST(Power on self test)代码,检测系统外围关键设备(如:CPU、内存、显卡、I/O、键盘鼠标等)。硬件配置信息及一些用户配置参数存储在主板的CMOS( Complementary Metal Oxide Semiconductor)上(一般64字节),实际上就是主板上一块可读写的RAM芯片,由主板上的电池供电,系统掉电后,信息不会丢失。

执行POST代码对系统外围关键设备检测通过后,系统启动自检程序,根据我们在BIOS中设置的启动顺序搜索启动驱动器(比如的硬盘、光驱、网络服务器等)。选择合适的启动器,比如通常情况下的硬盘设备,BIOS会读取硬盘设备的第一个扇区(MBR,512字节),并执行其中的代码。实际上这里BIOS并不关心启动设备第一个扇区中是什么内容,它只是负责读取该扇区内容、并执行,BIOS的任务就完成了。此后将系统启动的控制权移交到MBR部分的代码。

BIOS

MBR:
MBR的大小为512Bytes,位于HDD的第一扇区或最后扇区(取决于制造商)。

  1. MBR代表主引导记录;
  2. 它位于可引导磁盘的第一个扇区中,通常是/dev/hda或/dev/sda;
  3. MBR的大小为512字节,它三部分组成:

主引导加载程序代码(446Bytes): 此代码提供引导加载程序信息和硬盘上实际引导加载程序代码的位置详细信息。这有助于CPU加载Boot loader的第二阶段;
分区表信息(64Bytes): MBR包含64字节的数据,用于存储分区表信息,例如每个分区的开始和结束,分区大小,分区类型(无论是主分区还是扩展分区等);
Magic Number(2Bytes): 作为MBR的验证检查。

Bootloader:

我们知道不同操作系统的文件系统格式不同?还有我们知道一个磁盘可以安装多个操作系统,boot loader怎么能够做到引导的就是我们想要的操作系统呢?这么多不同的功能单靠一个446字节的boot loader是远远不够的。因此必须弄一个相对应的程序来处理各自对应的操作系统核心文件,这个程序就是操作系统的loader(注意不是MBR中的boot loader),这样一来bootloader只需要将控制权交给对应操作系统的loader,让它负责去启动操作系统就行了。

下图能更好地解释Bootloader的作用:
15418490888864.jpg

解读上图内容,我们知道一个硬盘的每个分区的第一个扇区叫做bootsector,这个扇区存放的就是操作系统的loader,所以我们常说一个分区只能安装一个操作系统,如上图,第一个分区的boot sector存放着windows的loader,第二个分区放着Linux的loader,第三个第四个由于没有安装操作系统所以空着。至于MBR的bootloader是干嘛呢, bootloader有三个功能:提供选单,读取内核文件,转交给其他loader

提供选单就是给用户提供一张选项单,让用户选择进入哪个操作系统;

读取内核文件,我们知道系统会有一个默认启动的操作系统,这个操作系统的loader在所在分区的boot sector有一份,除此之外,也会将这个默认启动的操作系统的loader复制一份到MBR的boot loader中,这样一来MBR就会直接读取boot loader中的loader了,然后就是启动默认的操作系统;

转交个其他的loader,当用户选择其他操作系统启动的时候,bootloader会将控制权转交给对应的loader,让它负责操作系统的启动。

MBR使用16Bytes来存储单个分区信息,这就是为什么MBR概念中的分区数限制为4(4×16 = 64)的原因。

64个字节包含4个分区(16x4)的分区信息。这就是为什么硬盘只能有4个主分区,因为MBR只能存储4个分区的信息。因此,如果硬盘上需要4个以上的分区,则必须扩展其中一个主分区,并在这些扩展分区之外创建逻辑分区。
15418480003742.jpg
它还包含有关GRUB(或旧系统中的LILO)的信息,因此,简单来说,MBR加载并执行GRUB引导加载程序。

注意: 现在MBR直接无法加载内核,因为它不知道文件系统的概念,并且需要为每个支持的文件系统提供带文件系统驱动程序的引导加载程序,以便引导加载程序本身可以理解和访问它们。

为了克服这种情况,GRUB与/boot/grub.conf和文件系统驱动程序中的文件系统的详细信息一起使用。

第二阶段GRUB启动引导

15417779713097.jpg
一旦Bootloader阶段1完成并且能够找到实际的引导加载程序位置,第1阶段引导加载程序通过将Bootloader加载到内存中来启动第二阶段。

在此阶段,位于MBR之后的前30KB硬盘中的GRUB(Grand Unified Bootloader)被加载到RAM中以读取其配置并显示GRUB引导菜单(用户可以手动指定引导参数)到用户。

GRUB将用户选择的(或默认)内核加载到内存中,并将控制权传递给内核。如果用户没有选择操作系统,则在定义的超时后,GRUB将在内存中加载默认内核以启动它。

根据gnu.org说的"引导加载程序是计算机启动时运行的第一个软件程序"。GRUB或GRand Unified Bootloader是Linux操作系统的引导加载程序。Grub有两个主要版本(Grub版本1和2)。目前,大多数linux ditros已经开始使用grub版本2. grub的一个主要特性是它可以使用linux映像安装,并且不需要运行操作系统。

Grub是一个多阶段引导程序(Stage1,Stage1.5和Stage2),grub版本1和版本2的3个阶段解释如下。

GRUB第1阶段:

GRUB第1.5阶段:

GRUB第2阶段:

原始文件是/etc/grub.conf,您可以在/boot/grub/grub.conf中查看符号链接文件:

# 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 CentOS (2.6.32-431.el6.x86_64)
    root (hd0,0)
    kernel /vmlinuz-2.6.32-431.el6.x86_64 ro root=UUID=73f96693-ed87-4953-9b51-d6f2cca370eb 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
    initrd /initramfs-2.6.32-431.el6.x86_64.img

正如您从上面的信息中注意到的那样,它包含内核和initrd映像,因此,简单来说,GRUB只是加载并执行内核和initrd映像。

grub> root (hd0,0) ---> root指令为grub指定了一个根分区
grub> kernel /vmlinuz-2.6.32-431.el6.x86_64 ro root=UUID=73f96693-ed87-4953-9b51-d6f2cca370eb .... ---> 加载指定的模块
grub> initrd /initramfs-2.6.32-431.el6.x86_64.img ---> 指定initrd文件

第三阶段内核引导

15418508016248.jpg
如阶段二所述,grub>boot指令后,系统启动的控制权移交给kernel。Kernel会立即初始化系统中各设备并做相关配置工作,其中包括CPU、I/O、存储设备等。

关于设备驱动加载,有两部分:

一部分设备驱动编入Linux Kernel中,Kernel会调用这部分驱动初始化相关设备,同时将日志输出到kernel message buffer,系统启动后dmesg可以查看到这部分输出信息;
另一部分设备驱动并没有编入Kernel,而是作为模块形式放在initrd(ramdisk)中。

initrd是一种基于内存的文件系统,启动过程中,系统在访问真正的根文件系统时,会先访问initrd文件系统。将initrd中的内容打开来看,会发现有bin、devetc、lib、procsys、sysroot、init等文件(包含目录)。其中包含了一些设备的驱动模块,比如scsi ata等设备驱动模块,同时还有几个基本的可执行程序insmod,modprobe,lvm,nash。主要目的是加载一些存储介质的驱动模块,如上面所说的scsi ideusb等设备驱动模块,初始化LVM,把根文件系统以只读方式挂载。

initrd中的内容释放到rootfs中后,Kernel会执行其中的init文件,这里的init是一个脚本,由nash解释器执行。这个时候内核的控制权移交给init文件处理,我们查看init文件的内容,主要也是加载各种存储介质相关的设备驱动。

驱动加载后,会创建一个根设备,然后将根文件系统以只读的方式挂载。这步结束后释放未使用内存并执行switchroot,转换到真正的根上面去,同时运行/sbin/init程序,开启系统的1号进程,此后系统启动的控制权移交给init进程。关于switchroot是在nash中定义的程序。

Linux Kernel需要适应多种不同的硬件架构,但是将所有的硬件驱动编入Kernel又是不实际的,而且Kernel也不可能每新出一种硬件结构,就将该硬件的设备驱动写入内核。实际上Linux Kernel仅是包含了基本的硬件驱动,在系统安装过程中会检测系统硬件信息,根据安装信息和系统硬件信息将一部分设备驱动写入initrd。这样在以后启动系统时,一部分设备驱动就放在initrd中来加载。

总结过程如下:

四阶段init初始化

15417782232838.jpg
init进程起来后,系统启动的控制权移交给init进程, /sbin/init进程是所有进程的父进程,当init起来之后,它首先会读取配置文件/etc/inittab,进行以下工作:

这时呈现给用户的就是最终的登录界面, 至此,系统启动过程完毕。

说明系统启动运行级别的概念以及服务的定制方法:

initrd可以正常检测和装载之后,最后的工作就基本上由操作系统来进行了。当系统的init进程起来之后系统启动的控制权移交给init进程。

/sbin/init进程是所有进程的父进程,当init起来之后,它首先会读取配置文件/etc/inittab,判断运行级别。

id:3:initdefault:

id:runlevel:action:process

id: 是用于标识此文件中的条目的唯一字符序列
runlevels: 是该条目适用的运行级别
action: 是对运行级别采取的操作
process: 指定要执行的进程

runleve:共7级别 为0-6,默认级别为3

runlevel 含义
0 关机
1 单用户模式(root, 无须登录), single, 维护模式;
2 多用户模式,会启动网络功能,但不会启动NFS;维护模式;
3 多用户模式,正常模式;文本界面;
4 预留级别;可同3级别;
5 多用户模式,正常模式;图形界面;
6 重启

还有一些额外的运行级别,很少使用,例如:

s,S or single: 单用户模式
emergency: 绕过rc.sysinit

总结运行级别:

运行级别1用于维护目的,因为这是一个非常有限的运行级别。只有最小脚本在此运行级别中运行。只有root用户才能登录。没有其他用户可以登录此运行级别。运行级别2比运行级别1更宽松。这里,所有用户都可以登录,但网络服务没有运行。Runlevel 3提供了完整的工作环境。所有用户都可以登录,启用网络。运行级别4仅用于实验目的。在运行级别5中,可以使用图形控制台。运行级别'0'是系统的暂停状态,切换到运行级别6将重启系统。

action 含义
wait 切换至此级别运行一次
respawn 此process终止,就重新启动之
initdefault 设定 init 默认运行级别
sysinit 设定系统初始化方式,/etc/rc.d/rc.sysinit
.... 以上为常见action

然后init将执行/etc/rc.d/rc.sysinit脚本,这是init在引导过程中执行的第一个脚本。

该脚本仅在引导期间执行一次,它将执行以下操作:

完成rc.sysinit后,内核会查看/etc/rc.d/rcx.d/目录(X是从/etc/inittab获取的运行级别)。

/etc/rc.d目录下有与每个运行级别对应的目录。根据这一行,运行/etc/rc.d/rc3.d目录中的脚本。我们列出这个目录中的文件。(其他目录如rc1.d,rc2.d ......中也有类似的文件)。

[root@Centos6.9 ~]# ls -l /etc/rc.d/rc3.d/
总用量 0
lrwxrwxrwx. 1 root root 19 9月  19 2016 K10saslauthd -> ../init.d/saslauthd
lrwxrwxrwx. 1 root root 22 11月 17 2016 K14zabbix-agent -> ../init.d/zabbix-agent
lrwxrwxrwx  1 root root 22 5月   9 2018 K15htcacheclean -> ../init.d/htcacheclean
lrwxrwxrwx  1 root root 15 5月   9 2018 K15httpd -> ../init.d/httpd
lrwxrwxrwx. 1 root root 18 11月 15 2016 K15svnserve -> ../init.d/svnserve
lrwxrwxrwx. 1 root root 20 9月  19 2016 K50netconsole -> ../init.d/netconsole
lrwxrwxrwx. 1 root root 15 11月 15 2016 K50snmpd -> ../init.d/snmpd
lrwxrwxrwx. 1 root root 19 11月 15 2016 K50snmptrapd -> ../init.d/snmptrapd
lrwxrwxrwx. 1 root root 14 11月 15 2016 K74ntpd -> ../init.d/ntpd
lrwxrwxrwx. 1 root root 17 11月 15 2016 K75ntpdate -> ../init.d/ntpdate
lrwxrwxrwx. 1 root root 21 9月  19 2016 K87restorecond -> ../init.d/restorecond
lrwxrwxrwx. 1 root root 15 9月  19 2016 K89rdisc -> ../init.d/rdisc
lrwxrwxrwx  1 root root 18 2月  23 2018 K92iptables -> ../init.d/iptables
lrwxrwxrwx. 1 root root 19 9月  19 2016 S08ip6tables -> ../init.d/ip6tables
lrwxrwxrwx. 1 root root 17 9月  19 2016 S10network -> ../init.d/network
lrwxrwxrwx. 1 root root 16 9月  19 2016 S11auditd -> ../init.d/auditd
lrwxrwxrwx. 1 root root 17 9月  19 2016 S12rsyslog -> ../init.d/rsyslog
lrwxrwxrwx. 1 root root 15 9月  19 2016 S25netfs -> ../init.d/netfs
lrwxrwxrwx. 1 root root 19 9月  19 2016 S26udev-post -> ../init.d/udev-post
lrwxrwxrwx  1 root root 14 5月  21 02:16 S55sshd -> ../init.d/sshd
lrwxrwxrwx  1 root root 15 5月   9 2018 S59Redis -> ../init.d/Redis
lrwxrwxrwx  1 root root 17 5月  11 2018 S69Tengine -> ../init.d/Tengine
lrwxrwxrwx  1 root root 14 7月  23 06:09 S80exim -> ../init.d/exim
lrwxrwxrwx  1 root root 15 7月  23 06:09 S90crond -> ../init.d/crond
lrwxrwxrwx. 1 root root 11 9月  19 2016 S99local -> ../rc.local

此目录中的某些文件以S开头,其他文​​件以K开头。以S开头的文件对应于必须在该特定运行级别中启动的脚本,而具有K的文件对应于要被杀死的脚本。这些文件只是/etc/rc.d/init/d目录下脚本的软链接(一个软链接指向/etc/rc.local,它本身是/etc/rc.d/rc/local的软链接。)。/etc/rc.d/init.d/中的脚本是守护进程。守护进程是在后台运行并提供某种服务的进程。例如,http守护进程(httpd)提供Web服务。

执行所有这些脚本后,将运行/etc/rc.local脚本,init运行在/etc/rc.d/rc.local中找到的任何内容(无论运行级别如何)。 rc.local非常特别,每次更改运行级别时都会执行它。它是在初始化过程或甚至启动过程中运行的最后一个脚本。一切都完成后,控制权将返回给内核。

注意: rc.local不用于所有发行版,例如Debian就没有这个文件。

如果您希望在系统启动时执行命令或脚本,则可以将其放在此脚本中:

[root@Centos6.9 ~]# cat /etc/rc.local 
#!/bin/sh
#
# This script will be executed *after* all the other init scripts.
# You can put your own initialization stuff in here if you don't
# want to do the full Sys V style init stuff.

touch /var/lock/subsys/local

# start solution service
/data/app/data-solution-service/solution-start.sh

现在所有这些脚本都已成功执行.

启动终端
接下来会由/sbin/mingetty指令启动终端,由于系统设置启动tty1-tty6 ,所以会启动6个命令行终端。最终呈现给我们的就是这样一个画面:
15426333668900.jpg

五阶段用户登录

/bin/login通过读取/etc/passwd文件, 成功登录后启动交互式登录shell 。此shell调用通常在启动时读取/etc/profile及其私有等配置项~/.bash_profile, ~/.bash_login, ~/.profile

通常使用shell程序(例如[prompt]$/bin/bash)或/bin/su命令在命令行启动交互式非登录shell 。还可以在图形环境中使用诸如xterm或konsole之类的终端程序启动交互式非登录shell 。这种类型的shell调用通常会复制父环境,然后读取用户的~/.bashrc文件以获取其他启动配置说明。

当shell脚本运行时,通常会出现非交​​互式shell。它是非交互式的,因为它正在处理脚本而不是等待命令之间的用户输入。对于这些shell调用,仅使用从父shell继承的环境。

该文件~/.bash_logout不用于shell的调用。当用户退出交互式登录shell时,它将被读取并执行。

许多发行版/etc/bashrc用于非登录shell的系统范围初始化。此文件通常从用户的~/.bashrc文件中调用,而不是直接构建到bash本身。本节遵循此约定。

/etc/profile:

该文件首先设置一些辅助函数和一些基本参数。它指定了一些bash历史参数,并且出于安全考虑,禁用为root用户保留永久历史文件。它还设置默认用户提示。然后它调用目录中的小型单用途脚本/etc/profile.d以提供大部分初始化。

更多参考:http://www.linuxfromscratch.org/blfs/view/6.3/postlfs/profile.html

参考: http://blog.51cto.com/13570193/2090653
参考: https://linoxide.com/booting/boot-process-of-linux-in-detail/
参考: https://www.linuxnix.com/linux-booting-process-explained/

WeZan